工程化,简单来说就是使小作坊生产变成流水线生产,实现自动化、模块化、性能优化等。
自动化可以通过命令行来实现。
实现从SCSS到CSS的自动化
- 全局安装Node-sass node-sass
npm install -g node-sass
(事实上SASS是Ruby社区写的) - 为了演示实现过程,我先把目录已有的一个style.css后缀改成style.scss
mv style.css style.scss
这样直接改后缀是没有问题的,因为SCSS是完全兼容CSS的,它只是在CSS语法上加上一些更高级的用法。 - 现在可以用node-sass把SCSS翻译成CSS了
node-sass style.scss style.css
实际上现在只是将排版变得漂亮了一点,因为我们并没有改SCSS的内容。 - 在style.scss写一些新语法,例如:
.topNavBar{
nav {
padding-top: 5px;
> ul {
list-style: none;
margin: 0;
padding: 0;
> li {
float: left;
margin-left: 17px;
margin-right: 17px;
position: relative;
> a {
font-size: 12px;
color: inherit;
font-weight: bold;
border-top: 3px solid transparent;
border-bottom: 3px solid transparent;
padding-top: 5px;
padding-bottom: 5px;
display: block;
position: relative;
}
}
}
}
}
- 再次运行命令
node-sass style.scss style.css
,可以看到style.css里对应内容已经被翻译成CSS语法:
.topNavBar nav {
padding-top: 5px; }
.topNavBar nav > ul {
list-style: none;
margin: 0;
padding: 0; }
.topNavBar nav > ul > li {
float: left;
margin-left: 17px;
margin-right: 17px;
position: relative; }
.topNavBar nav > ul > li > a {
font-size: 12px;
color: inherit;
font-weight: bold;
border-top: 3px solid transparent;
border-bottom: 3px solid transparent;
padding-top: 5px;
padding-bottom: 5px;
display: block;
position: relative; }
- 由于不能直接引入style.scss,现在每次改style.scss都要执行一次命令将其翻译成CSS语法,这样写岂不是很傻?
- 使用
-w
监听style.scss的变动,每次style.scss有改动都自动翻译
node-sass style.scss style.css -w
如此就实现了从SCSS到CSS的自动化。
Babel自动将下一代JS代码变成浏览器兼容的JS代码
- 我在写JS的时候已经用到了很多新的语法,当把代码下发给很多用户时,总会有不兼容的浏览器,比如IE。我需要将使用了新语法代码变为浏览器兼容的代码
- Babel命令行可以实现这个需求。
- 安装Babel
npm install --save-dev babel-cli
安装之前应该有package.json
,如果没有,新建一个。
npm init
可以创建一个合法的package.json
。
安装完成后检查一下package.json,如果多出下面代码,说明已经成功将Babel安装到项目中了。
{
"devDependencies": {
+ "babel-cli": "^6.0.0"
}
}
- 在package.json里加代码:(如果有上一句,记得写逗号)
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
- 根据文档运行
npm run build
,报错。
为什么呢?因为运行npm run build
就是在运行前面加的代码babel src -d lib
,也就是运行npm run build
和直接运行babel src -d lib
没有任何区别。而我们的Babel是局部安装的,安装在./node_modules/.bin/babel
,路径没有写全当然会报错。
运行./node_modules/.bin/babel src -d lib
相当于第4步npm在scripts里加入的一句代码,反过来说,当npm运行第4步所加的这句话的时候会优先认为babel是./node_modules/.bin/babel
,会优先找这个路径。
命令行的实质就是文件,全局安装和项目安装的区别看这里 -
babel src -d lib
的意思是把src目录的JS全部翻译成兼容浏览器的JS,放在lib目录里。d,destination。命令行基础要扎实,从一个字母猜到它的意思。
例如,运行./node_modules/.bin/babel js -d dist
,将js目录里的文件翻译到dist目录里 - 页面中要引入/js的地方路径改为/dist,也就是引入dist里面的js,而不是js里面的js
- 我们又遇到了跟上文写SASS一样的问题,每次改写js都要再执行一次命令,很傻。babel也提供了跟SASS一样的命令,让它自动翻译:
./node_modules/.bin/babel js -d dist --watch
整理代码
按照前端一般的规范,项目里有两个大目录,src(source)放未经翻译的代码,dist(distribution)放待发布代码。如果有第三方代码,放在node_modules里。
把所有html,SCSS,js,包括img都放进src中。
mkdir src
mkdir src/css
mv *.html src/
mv *.scss src/css
mv js/ src/
mv img src/
现在src目录中包含一个首页、一个css目录、一个js目录、一个img目录,整齐多了。(整理完要改引入文件的路径)
虽然整齐了,但是现在代码不能运行了,必须要做翻译。
进程1:
先用node-sass将src/css/
翻译到dist/css/
并监听,输出到一个目录里要用-o
node-sass src/css -o dist/css -w
进程2:
再用Babel将src/js/
翻译到dist/js/
并监听
./node_modules/.bin/babel src/js/ -d dist/js/ --watch
进程3,进程4:
此外,index.html和img目录也需要被监听,每次有变化就拷贝到dist目录,这又需要两个进程。watch-cli
以上所实现的自动化,是因为前端比较分裂,有人喜欢写css,有人喜欢写sass;有人喜欢写ES5的JS,有人喜欢写ES6的JS,等等。用翻译工具把它们自动翻译成兼容浏览器的代码,就是自动化。
如果有更复杂的需求,比如dist发布时加版本号,一旦有需求自动改版本号,则需要开更多的进程。请看Cache-Control 如何更新缓存
这一大堆进程可以用一个工具实现,一开始是Grunt,后来被更快的Gulp替代,再后来又被Webpack替代。
配置Webpack
- 在一个安全的目录中演示
mkdir webpack-demo
cd webpack-demo
npm init -y
npm install webpack@3 webpack-cli --save-dev
演示webpack3
现在可以看到package.json中多了一条依赖:
"devDependencies": {
"webpack": "^3.12.0",
"webpack-cli": "^3.0.1"
}
- 创建配置文件
touch webpack.config.js
- 把
src/index.js
输出为dist/bundle.js
,创建演示需要的目录和文件
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
- 试一下,在index.js里写入
let a = 1
console.log(a)
执行命令npx webpack
或node_modules/.bin/webpack
可以看到当前目录里出现了dist/bundle.js
,可以在这个文件中找到index.js里的代码,现在webpack可以做简单的JS拷贝工作了。
- 继续写配置,让JS兼容浏览器,让let变成var
babel-loader 7.x
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}
]
}
再次运行npx webpack
,报错,找不到什么就npm install 找不到的东西
如果是找不到env
,npm install @babel/preset-env
运行成功后,dist/bundle.js
中的let
已经变成var
了
- 使用模块化
在当前目录下src/js目录里创建module-1.js,module-2.js和app.js三个文件:
//module-1.js
function fn(){
console.log(1)
}
export default fn //如果有人引用module-1.js,就默认把fn传给他
//module-2.js
function fn(){
console.log(2)
}
export default fn
//app.js
import x from './module-1'
import y from './module-2' //x,y就是module-1,module-2返回的fn
x()
y()
然后只需要把app.js翻译成浏览器兼容的JS即可。更改webpack配置,把src/js中的三个JS文件翻译到dist/js/bundle.js中:
const path = require('path');
module.exports = {
entry: './src/js/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist/js')
}
};
再次运行./node_modules/.bin/webpack
,可以看到结果三个文件输出到了一个文件中:
bundle.js 3.27 kB 0 [emitted] main
[0] ./src/js/app.js 343 bytes {0} [built]
[1] ./src/js/module-1.js 141 bytes {0} [built]
[2] ./src/js/module-2.js 141 bytes {0} [built]
页面中只需要引入一个bundle.js就可以了。
这样就实现了JS的模块化有JS自己来控制,而不是在html里面控制。
- 用sass-loader把SCSS翻译成CSS:
安装sass-loader:npm install sass-loader node-sass webpack --save-dev
安装style-loader和css-loader:npm install style-loader css-loader --save-dev
写配置:
module: {
rules: [
{......}, //前面已有的配置
{
test: /\.scss$/,
use: [ //从下往上
"style-loader", // 用JS字符串创建style节点
"css-loader", // translates CSS into CommonJS 把css变成字符串
"sass-loader" // 把sass编译成css
]
}]
}
模块化:把src/css/main.scss
加载到dist/js/bundle.js
中变成一个字符串,等bundle.js运行的时候把这个字符串放到页面里的style标签里。(css可以用字符串的形式存储,例如皮卡丘)
// src/js/app.js
import x from './module-1.js'
import y from './module-2.js'
import '../css/main.scss' //css也从这里引入
x()
y()
- 自动加前缀 postcss-loader
安装postcss-loadernpm i -D postcss-loader
新建postcss.config.js写配置:touch postcss.config.js
module.exports = {
//parser: 'sugarss',
plugins: {
'postcss-import': {},
'postcss-cssnext': {},
'cssnano': {}
}
}
写webpack.config.js:
use: [
"style-loader",
"css-loader",
"postcss-loader", //必须写在style-loader和css-loader之后,其他之前
"sass-loader"
]
再运行./node_modules/.bin/webpack
,看报错,找不到什么就安装什么。
webpack每次需要实现一个功能都需要写配置,很不方便。可以使用不需要写配置的parcel代替它。