舒适的前端开发环境是怎样一种体验
舒适的前端开发环境是怎样一种体验
去年一篇《在 2016 年学 JavaScript 是一种什么样的体验?》吓坏了很多想要入行新同学和入行很久的老司机,感觉一下子前端世界已经看不懂了,做个页面要那么麻烦?当然如果你只是想要一个简单的静态页面,这么玩儿就是杀鸡用牛刀了。但如果你准备开发一个web app,之后会不断的迭代,有一个舒适的开发环境是及其重要的,那么底怎么样的环境才会是舒适愉悦的呢?
比如这样的一个环境:资源依赖可以安装并模块化引用、可以使用很酷的es6语法、可以使用scss预处理器写css、代码可实时更新而不用一遍遍的手动刷新页面,这样的开发环境你会不会觉得很爽!好,我们这就来配置一个这样的环境!
基础环境
首先,你需要一个node,然后npm也会随着node一起装上。
什么是npm?简单的说npm是用来下载安装nodejs的第三方工具包的一个管理器。当然,现在也可以安装浏览器中使用的包。提到包管理器,就不得不说下bower,bower之前一直是前端库管理工具,一开始npm只能发布和安装nodejs的包,所以bower盛行一时,随着commonjs的普及,以及umd规范的出现,让npm安装前端浏览器js包成为了可能,随着npm生态的成熟,bower也就慢慢被人淡忘了~
node安装完成后,可以执行以下命令验证安装是否成功:
$ node -v
v6.11.0
$ npm -v
3.10.10
别急,node的部分还没完,国内通过npm的官方源安装依赖好像很慢,动不动就要等上半天,如何解决?我们可以装一个nrm!nrm是npm registry管理工具,可以自由切换npm registry,然后命令行使用时依然是npm
,国内有很多npm的镜像,比如淘宝的cnpm,然而很多公司都架设了自己的私库。什么是私库?私库就是只能在公司内网访问,不能发布到npm共享平台的npm包,比如我们大公司私库的registry的名称就是hnpm
。不细说了,我们先装一个试试:
$ npm install -g nrm
然后根据官方教程我们先切一个国内的registry,比如大淘宝的:
$ nrm use cnpm
然后用npm随便安装个什么,看看速度如何?是不是很快_
等等,node还有。有的开发依赖包是有node版本依赖的,我们知道node不同大版本的功能还是差别很大的,但我们又不会一遍遍的卸载安装吧?感觉好蠢!好吧,我们当然可以装一个nvm,nvm?好像和nrm很像!nvm是node的版本管理工具,可以在多个终端切换和运行不同的node版本,可以到这里参考具体的安装教程。不过nvm在windows下不能使用,没关系,这里还有几个替代工具:nvm-window,gnvm供你选择。
同样,我们执行下命令验证安装成果:
$ nvm --version
0.33.0
项目初始化
有了上面的工具我们就可以开始创建一个项目了,我们执行以下命令来开始一个项目:
mkdir my-app
cd my-app
npm init
执行npm init
后你会看到你需要输入项目的一些信息,完成后回车确认,然后npm会在根目录下创建一个叫package.json
的文件,你之后通过--save
或者--save-dev
安装的依赖包都会出现在这个文件里。
先不管那么多,我们在根目录下创建一个src
目录,然后在src
下创建index.js
、index.html
……,好吧,你可以按照下面的结构新建文件:
.
├── package.json
└── src
├── index.css
├── index.html
└── index.js
在以下文件中输入代码:
index.js:
var el = document.createElement('div'),
text = document.createTextNode('My App');
el.appendChild(text);
document.body.appendChild(el);
index.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>My App</title>
</head>
<body>
</body>
</html>
我们要想办法让这个页面跑起来,what??? 就这么简单?,把js引入index.html
不就完事儿了嘛?当然没那么简单,我们可是要搞高大上的东西的呢!
哈~跑题了,我们继续。
首先我们要装一个叫webpack的东西,它是一个模块打包器,也就是我们俗称的构建工具,之前的那些grunt,gulp也都是构建工具,但是这年头流行webpack了!开个玩笑,webpack的可扩展性和可插件化,以及把任何文件都视为模块的概念得到了前端社区的一致推崇,而且在打包效率和按需分割文件上都是其他几个构建工具无法相比较的,当然webpack的配置太灵活,官方文档写的太太太难看懂,也导致了很多初学者无从下手。
接下来我们就来配下这个神奇的工具吧。
自动构建
我们先安装下webpack:
npm install --save-dev webpack
然后在根目录下新建一个webpack.config.js
文件,输入以下代码:
let path = require('path');
module.exports = {
entry: {
app: path.resolve(__dirname, 'src', 'index.js')
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
};
但要想在浏览器中访问还得有个本地服务器,好在webpack都帮我们想到了,我们可以装一个webpack-dev-server:
npm install --save-dev webpack-dev-server
我们在package.json中增加个npm scripts:
"scripts": {
"start": "webpack-dev-server --port 3003"
},
ok!我们执行下npm start
,在浏览器中访问:http://localhost:3003。哎?好像哪里不对!是的,你得告诉webpack,你的bundle(打包后的js)要插入到哪个html模板,前面说过,webpack是插件化的,它把很多功能开放给了第三方来实现,他只是来负责拼装的,好,现在我们需要安装一个html-webpack-plugin插件:
npm install --save-dev html-webpack-plugin
修改下webpack-config.js:
let HtmlWebpackPlugin = require('html-webpack-plugin'),
path = require('path');
module.exports = {
entry: {
...
},
...
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src', 'index.html')
})
]
}
再次执行npm start
,页面可以正常访问了。
但是,这样似乎有点low,我们新增一个文件utils.js
,搞点es6语法:
.
├── package.json
└── src
├── index.css
├── index.html
├── index.js
+ └── utils
+ └── utils.js
utils.js:
export function wordsToSentence(...words) {
return words.join(' ');
}
修改index.js
+ import { wordsToSentence } from './utils/utils';
let el = document.createElement('div'),
- text = document.createTextNode('My App');
+ text = document.createTextNode(
+ wordsToSentence('Welcome', 'to', 'my', 'app!')
+ );
el.appendChild(text);
document.body.appendChild(el);
刷新页面后好像也没什么异常(你肯定用了chrome吧!),仔细看控制台的source的app.js
(你的bundle)的代码片段:
"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = wordsToSentence;
function wordsToSentence(...words) {
return words.join(' ');
}
值得注意的是,使用es6时需要考虑那些没有支持es6的旧浏览器,虽然在chrome或者其他高级浏览器中没有出现问题,但不能保证在其他浏览器中能正常运行。为了万无一失,我们需要将es6转换为es5,也就是js代码转换器,这类工具当今世界就属babel最牛逼了:
npm install --save-dev babel-loader babel-core
稍等,装了babel还没法用,还得搞个presets:
npm install --save-dev babel-preset-env
在根目录下新建个.babelrc
,输入配置:
{
"presets": ["env"]
}
修改webpack.config.js,增加babel的支持:
...
module.exports = {
...
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: path.resolve(__dirname, 'src')
}
]
},
...
};
执行npm start
,找到控制台source下的app.js代码片段:
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.wordsToSentence = wordsToSentence;
function wordsToSentence() {
for (var _len = arguments.length, words = Array(_len), _key = 0; _key < _len; _key++) {
words[_key] = arguments[_key];
}
return words.join(' ');
}
已经成功转换成es5代码。但是,目前es6 modules是由babel来转的,你可以对比前后2次的代码片段的模块输出部分。现在,webpack 2已经内4置了es6 modules的转换,据说效率和性能比babel高!_没验证过哦,我们先试试,把babel的模块转换关了先:
.babelrc
{
"presets": [
["env", {
"modules": false
}]
]
}
执行npm start
再次查看输出后的app.js的代码片段:
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.wordsToSentence = wordsToSentence;
+/* harmony export (immutable) */ __webpack_exports__["a"] = wordsToSentence;
function wordsToSentence() {
...
}
模块输出方式又回到了使用babel前的代码。
js的环境似乎已经准备就绪,但css还没上场,我们来修改下index.css:
#app {
color: #57af09;
}
同时将css导入bundle入口,并修改下index.js:
import './index.css';
import { wordsToSentence } from './utils/utils';
let el = document.createElement('div'),
...
el.id = 'app';
...
有了样式还不行,webpack还需要相应的loader来处理css的模块:
npm i --save-dev style-loader css-loader
修改下webpack.config.js:
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.css$/,
loader: ['style-loader', 'css-loader'],
include: path.resolve(__dirname, 'src')
}
]
},
...
};
执行npm start
,现在可以看到页面已经有了样式。但是,我们说过,我们希望使用先进的武器:scss。我们修改下index.css:
$app-color: #57af09;
#app {
color: $app-color;
}
再修改下文件后缀:
.
├── package.json
└── src
- ├── index.css
+ ├── index.scss
...
修改index.js的入口:
-import './index.css';
+import './index.scss';
由于文件(模块)类型变了,我们还需要一个sass的webpack loader:
npm install --save-dev sass-loader node-sass
再次修改webpack.config.js:
...
module.exports = {
...
module: {
rules: [
...
{
- test: /\.css$/,
+ test: /\.scss$/,
- loader: ['style-loader', 'css-loader'],
+ loader: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, 'src')
}
]
},
...
};
执行npm start
,webpack编译没有报错,页面显示一切正常!
代码自动更新(热更新)
如果你尝试修改index.scss
的样式,你有没注意到一个问题:页面会自动刷新。但有时候我们在开发一个模块,比如dialog,刷新会导致你需要反复的在页面上操作才能看到这个dialog的样式更新。那我们有没有办法不刷新页面又能看到代码的更新呢?
其实很简单,因为webpack-dev-server已经内置了这样的功能,我们只要配置下package.json
的npm scripts:
"scripts": {
"start": "webpack-dev-server --hot --inline --port 3003"
},
注意到上面的代码,我们增加了--hot --inline
,让开发环境有了热更新的能力。我们重新执行npm start
,然后将你的浏览器和编辑器并排放置,然后反复修改index.scss,你会看到页面不会刷新,但样式在自动的推送更新,这就是传说中的热更新。
结束语
到这里,简单(简陋)的、现代化的前端开发环境已经有了基本的雏形,但是,本篇文章不是webpack的使用指南,也不是es6的语法教程,尽管如此,还是希望你通过本篇文章感受到前端开发在工程化领域的发展带来的惊喜。