本文是[前端世界的超级英雄 —— 构建工具]系列的第一个专题,webpack计划会有三篇《超级英雄webpack :头领战士》,《超级英雄webpack :超能勇士》 《超级英雄webpack :领袖之证》。分别用webpack1,webpack3, webpack4。由浅入深的讲解webpack。本文会侧重webpack在项目中解决的问题与实战的应用。
1、知识点预览:
除了要讲故事,我们还是正经webpack教程,先梳理一下本文基本知识点:
2、webpack概述
好了,宝宝们,坐稳扶好。欢迎回到2012年的前端世界。
随着JavaScript技术的发展,基于CommonJS的后端模块化规范和基于AMD前端模块化规范相继诞生。node童鞋能够用npm包管理工具轻松的管理自己所依赖的第三方库。由于npm中的第三方库只有部分支持AMD规范,大部分第三方库,前端童鞋不得不手动下载,放到指定目录下,再去引用。如果遇到,不支持AMD规范的库, 还需要配置shim,非常凄惨。
著名的三大框架angular也在前一年诞生,web页面变成了一个能实现复杂交互,精美展示的完整应用程序。但随着而来的问题是,SPA(单页面应用)项目的体积越来越大,也没有办法动态加载CSS,通常我们会把CSS合并压缩成一个文件,但在糟糕的网络情况下,这简直是灾难。
这一年,在德国纽伦堡一个叫 科伯斯 (Tobias Koppers) 的工程师,正潜心研究Google Web Toolkit工具中代码拆分(code splitting)的功能。因为在这个谷歌为Java程序员开发的工具中,它可以实现延迟加载不需要的代码,这对于提高大型应用加载速度非常重要。但在当时JavaScript还没有能实现这一功能的开源工具。
不久后,科伯斯写出了这个可以“require anything”的前端打包工具—webpack。
好了,现在我们就开始用webpack去改造 snap.svg 的demo。(借鉴了 Webpack from First Principles )
3、webpack实战
虽然这只是个单页面,算不上单页面应用。但是存在的问题却是是一样的。
首先分析一下 代码 :
注意:要将chrome中Network下Disable cache打开,以免以后修改文件有缓存
3.1、 安装webpack
webpack是基于CommonJS的打包工具。我们首先给我们的项目安装webpack.(我们用的1.15.0版本是webpack1最后一个版本,2017年发布的。)
npm init //npm初始化
npm install webpack -g //全局安装webpack
npm install webpack@1.15.0 --save-dev //项目中安装webpack
touch webpack.config.js // 新建一个webpack配置文件
复制代码
我们新建一个webpack配置文件:webpack.config.js,然后配置入口文件和出口文件
module.exports = {
entry : './src/scripts/main.js', //配置入口文件
output : {
path : './build', //配置出口文件所在文件路径
filename : 'bundle.js' //配置出口文件名
复制代码
terminal中执行webpack
webpack
复制代码
然后在index.html文件中,我们讲main.js文件路径修改为src="build/bundle.js"。这样我们就实现了webpack的编译打包。
刷新浏览器能看到加载的bundle.js鳄鱼先生。我们将网速调整成3G,可以看到加载的文件数和加载速度。
问题:webpack可以讲main.js文件打包成另一个,但并有没有体现出什么优势啊?
3.2、 实现CommonJS模块化
我们知道webpack是基于CommonJS实现了js文件的模块化,那么我们就去可以用CommonJS的方式引入脚本文件,不用自己下载再放到指定目录里面了。 我们项目中用到了snapsvg-cjs这个库,我们就先删除原来手动引入的文件,然后再用npm去加载
rm -rf vendor //删除vendor文件夹
npm install --save snapsvg-cjs //npm加载snapsvg-cjs库
复制代码
然后我们在main.js中引入,var Snap = require('snapsvg-cjs'); 在index.html文中删除script标签引入的snapsvg-cjs.js文件
webpack //执行webpack打包
复制代码
我们可以看到这一次加载的文件数就变成了13个。
问题:现在我们前端童鞋也可以用npm包管理工具轻松管理自己依赖的第三方库了,但是既然是require anything,那么除了js脚本文件以外,还可以处理其他文件吗?
3.3、 Loader
webpack一个重要的机制就是可以通过不同的loader将css,图片等静态资源都打包进入js文件。
3.3.1、 url-loader
url-loader:会将引入的图片编码,生成dataURl,打包到文件中。
我们想把在main.js里通过路径传进去的/src/assets/crocodile.svg文件,通过require方式引入,引入图片需要依赖 url-loader 这个加载器。
npm install --save-dev url-loader //安装url-loader
复制代码
在webpack.config.js文件中加入loader的配置
module : {
loaders : [
test:/\.svg$/,
loader:'url'
在main.js中,将图片引入var crocodileUrl = require('../assets/crocodile.svg');
以变量的形式,传到Snap.load()中。
在终端里执行webpack打包
这样我们图片已经以base64的格式打包到bundle.js文件中,请求的时候通过data协议直接加载。
3.3.2、 style-loader与css-loader
css-loader: 加载.css文件
style-loader:配合css-loader使用,以形式在html页面中插入css代码
删除index.html中的main.css,在main.js中引入require('../styles/main.css');
npm install --save-dev css-loader style-loader //安装css-loader和style-loader
复制代码
在module配置css-loader和style-loader。
module : {
loaders : [
test: /\.(svg|jpg)$/, //因为css样式中有背景图片,所以我们匹配jpg
loader:'url'
test: /\.css$/,
loaders:[
'style',
'css'
webpack编译后,你会看到,css文件都被打包到了bundle.js中进行动态加载。这里留个问题,由于css动态加载,加载时就会存在白屏问题,这个我们该怎么解决呢?
学习到这你就理解了,“webpack可以通过不同的loader将css,图片等静态资源都打包进入js文件。”这句话的含义,这就是webpack“require anything”的具体实现。
问题:css可以通过动态加载引入index.html,那么main.js文件,有没有什么办法自动引入index.html中呢?
3.4、 Plugin
Plugin: 插件目可以解决 loader 无法实现的其他事。
3.4.1、 html-webpack-plugin
//安装html-webpack-plugin
npm install html-webpack-plugin --save-dev
//在webpack.config.js文件中引入
var HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [new HtmlWebpackPlugin()]
复制代码
执行webpack,你会发现在build文件夹中,多了一个index.html文件。我们已经成功将main.js打包进去了,
那么如何与之前的文件上加一个main.js呢。我们只需要配置它的模版参数就可以啦。
plugins: [new HtmlWebpackPlugin({
template: 'index.html'
复制代码
我们访问http://localhost:8088/build/index.html,就可以看到我们的鳄鱼先生了。
问题:还记得之前动态加载css白屏的问题吗?现在我们是似乎有办法解决了,那就是在把它从main.js文件中提出来就好了。这次我们该用什么plugin呢?
3.4.2、 extract-text-webpack-plugin
//安装html-webpack-plugin
npm install extract-text-webpack-plugin@1.0.1 --save-dev
//在webpack.config.js文件中引入
var ExtractTextPlugin = require('extract-text-webpack-plugin');
plugins: [new HtmlWebpackPlugin()]
然后我们配置loader
test: /\.css$/,
loader:ExtractTextPlugin.extract('style-loader','css-loader') //注意这里不是loaders
复制代码
这样我们就可以看到css被单独打包了呢。样式就先加载出来了。现在你可能会很好奇,为什么要在loader中还要去配置,webpack的工作流程是怎么样的?我们先在这里留个问题。
那你肯定又会问,这些loader,plugin,我怎么知道,它们都是做什么的呢,哈哈哈,google,百度啊。是不是觉得有点low,好吧,传送门awesome-webpack
问题:开发时每次都需要执行一个webpack去编译是不是很烦,那么webpack有没有什么解决方法呢,当然有,可以执行webpack --watch这样webpack就可以自动编译。这就幸福了?天真!我还想要浏览器也自动刷新,这时候webpack --watch就无能为力了。我们需要学习webpack-dev-sever
3.5 webpack-dev-server
sudo npm install webpack-dev-server //全局安装webpack-dev-ser
npm install webpack-dev-server@1.16.5 --save-dev //项目中安装webpack-dev-ser
webpack-dev-server //执行webpack-dev-ser
复制代码
访问http://localhost:8080/webpack-dev-server,现在我们就可以完成实时预览了。
注意:webpack-dev-server只做文件实时预览服务。不做打包服务。所以你不会看到编译后的文件。但是webpack-watch是可以看到。
但还有两个问题,一个是顶部有App ready看着比较别扭。这样的webpack-dev-server的配置问题,留到下一篇在webpack3中让《webpack :超能勇士》去解决吧。
问题:另一个是文件中引用的图片路径显然不对,我该如何去配置?
4、webpack常见配置
恭喜你,你已经进来了webpack配置工程师角色。在于webpack打交道的时候最多的就是,你要了解各种配置,去解决你项目中的问题。那么我们现在就来学习一些常见的配置。
1、处理html文件中的图片路径,配置html-loader
test: /\.html$/,
loader: "html-loader"
复制代码
2、配置多个入口文件Chunk
entry : {
'main' : ['./src/scripts/main.js'],
'page' : ['./src/scripts/page.js']
output : {
path : './build',
filename : './js/[name].js'
复制代码
webpack编译后,我们能看到打包好的main.js和page.js文件,它们也都被加载到index.html文件中
3、配置多页面文件,指定加载模块
我们想main.js和page.js分别被加载到index.html文件和page.html文件中,而不是都加载到index.html中
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
chunks: ['main']
new HtmlWebpackPlugin({
template: './src/page.html',
filename: 'page.html',
chunks: ['page']
复制代码
HtmlWebpackPlugin还有一个常用的参数是一个文件加hash。我们下一篇去配置
4、提取公共模块,webpack.optimize.CommonsChunkPlugin插件
比如base.js是一个公共模块,它需要被加载到每个页面中去。
我们需要新建一个关于base.js的chunk,'common' : ['./src/scripts/base.js']
在HtmlWebpackPlugin中添加进去, chunks: ['page','common']
引入webpack.optimize.CommonsChunkPlugin
var webpack = require('webpack');
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
filename: './js/base.js'
复制代码
webpack.optimize.CommonsChunkPlugin插件的作用是能自动分析每个页面引入的共同模块,并且都编译进base.js文件中。比如,在page.js和main.js文件中,我们都引用了main.css。
这样base.js就又两部分组成
base引用的chunk,即common
自动分析的公共模块,如果只想引用公共模块,可以把name定义为默认的commons
好了,这一篇就到这里了,是不是还有点不舍呢。宝宝们,跟鳄鱼先生说再见。下一篇《超级英雄webpack :超能勇士》