细说前端⾃动化打包⼯具--webpack
背景
记得2004年的时候,互联⽹开发就是做⽹页,那时也没有前端和后端的区分,有时⼀个⽹站就是⼀些纯静态的html,通过链接组织在⼀起。⽤过Dreamweaver的都知道,做⽹页就像⽤word编辑⽂档⼀样。⼀个html页⾯,夹杂着css,javascript是再常见不过的事了。
随着前端的不断发展,特别是单页应⽤的兴起,这种所见即所得的IDE⼯具,就渐渐地退出了前端的主流。⼀个应⽤,通常只有⼀个静态页⾯(index.html),甚⾄这个页⾯的body只有⼀个或少数⼏个div组成。
这时有⼤量的css和javascript代码需要编写。如何组织他们,就是现在前端所⾯临和要解决的问题。
⼀些很好的前端框架(像angularjs,React,Vue)可以帮我们如何合理的组织代码,保持代码的可维护性和扩展性。这在开发阶段是很有⽤的,可是要把应⽤发布到线上的时候,需要把代码进⾏合并压缩,以减⼩代码体积,和⽂件数量,⼈为的对代码进⾏丑化。于是就有了grunt,gulp,webpack等前端⼯程化打包⼯具。
如何使⽤webpack
使⽤webpack之前,需要安装node.js,然后通过npm 安装webpack.具体的安装过程。本着从⼊门到精通的顺序,先来看⼀个最简单的应⽤。
场景⼀:
在demo1⽬录下,有两个⽂件,app.js,cats.js,需要把它们合并成⼀个bundle.js⽂件.
cats.js:
var cats = ['dave', 'henry', 'martha'];
app.js:
cats = require('./cats.js');
console.log(cats);
如果是全局安装的webpack,那么直接在命令⾏窗⼝中输⼊webpack app.js bundle.js就可以了:
要得到压缩版的也很容易,在后⾯追加⼀个-p参数:
bundle.js由原来的1.58kb缩⼩到304b.
如果每改⼀次代码就要输⼀次命令,就太没意思了,这时就需要追加⼀个" -w " 参数 (watch) 监视代码的改动。
webpack app.js bundle.js -p -w
注意:如果是clone的代码,试验时,请移除⽬录下的fig.js⽂件。
虽然简单,但是这⾥有⼀个重要的概念要说⼀下:官⽅⽂档中把app.js这个⽂件称为“entry point”,即“⼊⼝”。代表着webpack从哪开始。webpack会顺着这个⼊⼝⽂件⾃动寻⾥边所依赖的⽂件,⽐如demo0
1中的cats.js会⾃动被载⼊。⽽bundle.js 是我们指定打包之后输出的⽂件名,默认的输出⽬录就是命令运⾏时所在的⽬录,也可以在指定输出⽬录,如./dist/bundle.js ,这样webpack就会⾃动创建dist⽬录,然后把bundle.js写在dist⽬录下。由于app.js这个⼊⼝⽂件是纯js,webpack直接就可以⽀持,如果是其它类型的⽂件,⽐如css,就需要⽤到"loader",即“加载器”,后⾯会有详细介绍。
除了直接⽤webpack命令指定⼊⼝⽂件打包之外,还可以通过配置fig.js⽂件实现同样的功能:
//最简单的webpack配置
entry: './app.js', //⼊⼝⽂件地址
output: {
filename: 'bundle.js', //打包后的⽂件名
}
};
通过配置fig.js之后,在命令⾏下只需要简单的输⼊webpack就可以了。如果是这么简单的应⽤,显然体现不出fig.js存在的价值。通常我们的⽹站都会有多个页⾯,⽐如index,home,about等等,每个页⾯都是⼀个独⽴的⼊⼝,于是就产⽣了多⼊⼝的情况,下⾯就看看多⼊⼝的情况下,webpack怎么输出不同的打包⽂件。
//fig.js
//多⼊⼝⽰例
entry: {
bundle1: './main1.js', //⼊⼝1
bundle2: './main2.js'//⼊⼝2
},
output: {
filename: '[name].js' // [name]是⼀个变量,会⾃动替换成entry的key
}
};
和demo01相⽐,这次的⼊⼝(entry)是⼀个对象, ⽤键值对的形式指定了多个⼊⼝⽂件,输出的⽂件名⽤了变量表⽰。事实上,⼊⼝⽂件的值还可以是数组。如:
//fig.js
//多⼊⼝⽰例
entry: {
bundle1: ['./main1.js'], //⼊⼝1
bundle2: ['./main2.js']//⼊⼝2
},
output: {
filename: '[name].js' // [name]是⼀个变量,会⾃动替换成entry的key
}
};
这种⽤法,对于⼊⼝⽂件需要指定多种类型的⽂件时⽐较有⽤。⽐如['./main1.js','./main1.css'],后⾯⽤到再细讲。⼩结⼀下:对于entry⼀共展⽰了三种形式:
1. entry:'app.js' 直接写⼊⼝⽂件
2. entry:{bundle:'./main1.js'} 对象形式
3. entry:{bundle:['./main1.js']} 对象中的值⽤数组表⽰
接下来的将展⽰webpack在jsx, es6 中的⽤法。这⼀节内容会稍稍有点多。⾸先是package.json⽂件,它不是webpack的组成部分,但是常和webpack项⽬出双⼊对,先看⼀下它的⼤概模样:
{
"name": "demo01",
"version": "1.0.0",
"description": "sample to use webpack",
"main": "index.html",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack"
},
"keywords": [
"webpack"
]
,
"author": "frog",
"license": "MIT",
”dependencies":{},
"devDependencies": {
"babel-core": "^6.20.0",
"babel-loader": "^6.2.10",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"react": "^15.4.1",
"react-dom": "^15.4.1",
"webpack": "^1.13.0"
}
}
关于这个⽂件的更多介绍,请移步这⾥我只重点介绍⼀下以下内个内容:
1. scripts 命令⾏脚本通过key:value的⽅式描述。key是脚本名,value是脚本执⾏的内容,通过在命令⾏中输⼊npm run 脚本名就可以执⾏。这⼀块的内容是实际开发中很实⽤的,这⾥不详情展开,
常见的脚本名有:npm run start , npm run test 。内置的脚本名(⽐如start),可以省略run。
2. devDependencies 开发依赖,相应的还有⼀个dependencies(可以理解为⽣产环境依赖)
通过npm install 包名 --save-dev (保存到devDependencies),或 --save 保存到(dependencies)
package.json是⽤来配合包的管理和发布⽤的,如果你不想发布这个项⽬,似乎以上内容对项⽬开发并没有什么好处,但是作为团队协作,它可以⽅便⾃⼰和同事快速搭建项⽬,管理项⽬中⽤到的第三⽅包。
下⾯回到fig.js这个⽂件来。由于jsx是react专⽤的语法,超出了js的语法范围,要想加载jsx⽂件,需要借助⼀个loader(加载器),不同类型的⽂件有不同的加载器,⽐如jsx,es6要⽤到babel-loader
加载css要⽤到css-loader,加载html要⽤到html-loader等等. 下⾯是具体的⽤法:
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
loaders:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query:{
presets:['react','es2015']
}
},
]
}
};
所有的loader都放在module下⾯的loaders⾥边.通常有以下内容:
1. test:是对该类⽂件的正则表达式,⽤来判断采⽤这个loader的条件。
2. exclude是排除的⽬录,⽐如node_modules中的⽂件,通常都是编译好的js,可以直接加载,因此为了优化打包速度,可以排除。作为优化⼿段它不是必须的。
3. loader: 加载器的名称,每⼀个加载器都有属于它⾃⼰的⽤法,具体要参考官⽅说明。
4. query: 传递给加载器的附加参数或配置信息,有些也可以通过在根⽬录下⽣成特殊的⽂件来单独配置,⽐如.babelrc
这⾥配置好,还不能⽤,需要安装对应的加载器到项⽬中来,安装⽅式很简单,通过命令⾏,输⼊npm install 加载器的名称 --save-dev 或 --save
加--save或--save-dev的⽬的是为了把该插件记录到package.json中去,⽅便通过npm install的时候⾃动安装。
通过npm3.0+版本安装的时候,它不会⾃动安装依赖,需要⼿动去安装,⽐如安装这个的时候,它提⽰要安abel-core和webpack,依次安装即可。demo03⽐较激进,直接⽤了jsx和es6的语法,所以要安装的插件⽐较多,但这也是实际开发中经常⽤到的。
"devDependencies": {
"babel-core": "^6.20.0",
"babel-loader": "^6.2.10",
"babel-preset-es2015": "^6.18.0",//es6转普通js⽤
"babel-preset-react": "^6.16.0", //解析jsx⽤
"react": "^15.4.1",
"react-dom": "^15.4.1",
"webpack": "^1.13.0"
}
由于我们在package.json的script中加了⼀个start脚本,所以这次,我不打算⽼套的⽤法,这次来点新鲜的尝试。直接运⾏npm start,看看是否⼤⼒出奇迹。
这和直接运⾏webpack是⼀样的结果,但是显得更⾼⼤上⼀些。如果你⼀半会不觉⽤不到react或es6这么新潮的东西,那就请忽略前⾯的内容,下⾯看⼀点更加简单更加常⽤的加载器
样式加载器
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
loaders:[
{ test: /\.css$/, loader: 'style-loader!css-loader' },
]
}
};
这⾥有两个要注意的地⽅:
1。对于有多个加载器串联的情况,webpack,它是从右向左依赖加载的,也就是说先⽤css-loader,再⽤style-loader.
2. 为什么会有⼀个style-loader,因为webpack默认是把css⽂件插在html⽂件内,通过style标签加载样式的。所以需要⽤style-loader这个加载器。
如果想要把css⽤⽂件的形式link到html中,也是可以的,后⾯会讲到。
由于我们⽤了css加载器,所以⼊⼝⽂件其实也可以直接写成:entry:'./app.css'; 效果是⼀样的。这就体现了,⼊⼝⽂件,不⼀定要是js格式,只要有对应的加载器,就可以直接在⼊⼝中使⽤,甚⾄多种类型混合使⽤,⽐如['./app.js','app.css'],都是可以的。
样式中,常常会⽤到图⽚,⽐如background:url('../images/logo.jpg'); 如果没有指定加载器,就会报错(you may need an appropriate loader to handle this file type),这时,就需要⽤到图⽚加载器了,不要以为,只有在⼊⼝中⽤到的⽂件才要加载器,只要是在webpack⼯作期间加载到的⽂件,只要不是js⽂件,就需要指定加载器,并在fig.js
中正确配置。
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
loaders:[
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192&name=[name][ext]'}
]
}
};
图⽚加载- ⽰例中⽤的是url-loader,并不是期望的image-loader, 原因是就可以加载图⽚字体这些⽂件了,
因此不需要重复造轮⼦,事实上,url-loader还不是最终的加载器,它只不过是对file-loader的进⼀步封装。通过在url-loader后⾯加?来挂载更多的配置参数,可以实现定制化的需求,⽐如对于图⽚⼩于8192字节的图⽚,采⽤base64的⽅式,直接输出在css中,可以减少http请求。对于⼤这个限制的图⽚,通过name指定输出的⽂件名,在前⾯指定路径也是可以的。⽐如/images/[name][ext] ,这⾥的[name]和[ext]都是变量的表⽰,前⾯有讲过,⽤在这⾥,表⽰⽤原来输⼊时的⽂件名和扩展名。需要注意的是,这个路径是参考默认的输出路径的来的。如果要指定输出路径怎么处理呢?
请参考以下⽅法:
1. 通过在fig.js 中指定,output:{path:'./dist',...}
entry: './src/app.js',
output: {
path: './dist',//新的输出路径
filename: 'app.bundle.js'
}
};
'./'代表项⽬的当前⽬录,通常指根⽬录,这是⼀种相对路径的表⽰,也可以⽤绝对路径,通过solve(__dirname,'./')来指定,这时,webpack所⽣成的js,css⽂件都会变
成./dist⽬录下,⽽对于本例中的图⽚,则还是在./⽬录下,
并没有把图⽚⽣成在dist⽬录下,试试 loader: 'url?publicPath=./dist/'
entry: ['./main.js','./icon.css'],
output: {
path:'./dist',
filename: 'bundle.js'
},
module: {
loaders:[
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192&publicPath=./dist/'},
{ test: /\.css$/, loader: 'style-loader!css-loader' },
]
}
};
通过指定这个publicPath实现了图⽚⽣成到指定的⽬录。同样的,通过在output中指定这个值也是同样的作⽤。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论