vue2 vue-router2 webpack1
2017-04-11 10:03
387 查看
转自:http://www.qinshenxue.com/article/20161106163608.html;
之前写了一篇如何搭建 vue 1.x 工程的文章《vue vue-router webpack》(以下简称为“上文”),在写上文的时候,vue 2还处于beta版本,现在已经正式发布了,在其发布后不久,我就尝试着按上文的步骤来搭建 vue 2.x 的工程,结论是上文的步骤基本上没什么大的问题,只有部分细节需要调整。为了保证整体步骤的完整性,本文依旧从零开始,以一个新手视角,尽可能详尽且不遗漏的阐述每个步骤,同时又为了精简步骤,上文中的一些无关痛痒的话就省略了,对于没有变化的步骤就直接复制上文。
注意:2017-03-14 将 vue 升至 2.2.4,本文会和 vue 最新版本(仅限 2.x.x)保持一致,升级所带来的影响点会记录在升级记录章节下。
新建目录结构如下,新增的目录及文件先空着,下面的步骤会说明添加什么内容。
其中output的各配置项作用如下:
path: './dist' 打包后js、css、image等存放的目录;
publicPath: '/dist/' 可以不配置,如果不配置则取默认publicPath: '/',在实际项目中,静态资源一般集中放在一个文件夹下,比如static目录,那么这里就应该改成publicPath: '/static/',相应的 index.html 中引用的 JS 也要改成src="/static/build.js",publicPath 可以解释为最终发布的服务器上 build.js 所在的目录,其他静态资源也应当在这个目录下。
filename: 'build.js' 打包的js文件名,index.html 引用的 JS 要和这里保持一致。
配置 webpack-dev-server,只需在package.json添加以下启动命令即可。
各命令参数作用如下:
--inline 一共两种模式,默认为iframe模式,inline和iframe模式最明显的区别就是访问路径的不同,iframe模式的访问路径是http://localhost:8080/webpack-dev-server/,实际上iframe模式页面嵌入的<iframe>的地址还是http://localhost:8080/,那岂不是可以直接访问,不禁想问为啥不直接访问呢?因为无论是哪种模式,都是为了做到修改代码后能自动刷新,其中iframe模式是在修改代码后,重新加载iframe,而--inline是刷新浏览器,本质上都是重新全部加载一遍
--hot iframe不需要配置,配置了反而不能正常刷新了,所以只能配合--inline使用,作用是开启热替换,修改代码后,浏览器只会重新加载修改的组件代码,不会全部重新加载
--open 自动打开浏览器
配置了server,习惯性的测试下上述配置是否成功,确保后续步骤在一个成功的基石上进行,执行npm install,安装完后执行npm run dev,浏览器会自动打开http://localhost:8080/,能访问(可以在index.html添加内容来确认是否启动成功)则说明上面的配置没问题。
接下来就要配置入口js,这个也是和vue 1.0区别比较大的地方,vue-router 1.0通过VueRouter.start(App,'#app')来初始化整个网站,这种方法在vue-router 2.0已经被废弃,因此需要我们来自己来完成new Vue()。main.js内容如下:
链接路由的不再使用<a v-link="{ path: '/about' }"></a>,改为组件的方式。app.vue内容如下:
还记得上文提到过,这时候启动会报错,提示一些依赖的loader未安装,果然:
不同于上文的是这次只报了一个错,下面会说明原因。在package.json添加:
安装后再次启动就成功了。
上文报错提示缺少很多vue相关的loader,难道新版不需要了?查看node_modules目录,发现vue-loader所依赖的模块都已经安装了(除了vue-template-compiler是自己添加的)。
原来是vue-loader中已经将所依赖的模块放到了其目录下的package.json中的dependencies,因此不需要自己去添加了,同时也发现vue-template-complier出现在peerDependencies下,这也解释了为什么第一次启动会报缺少它的原因。如图所示:
还有在app.vue文件中使用了ES6的语法(比如:const,箭头函数)也不会报缺少babel-loader,这也归功于vue-loader新加的vue-template-es2015-compiler。不过仅仅是部分语法,比如import等语法还是需要安装babel-loader。
安装后我们来验证一下,在about.vue中添加如下样式:
运行效果如图所示:
只在vue文件中使用嵌入式(<style>)来添加CSS代码,是不用在webpack.config中配置loader的,可以理解为没有增加新的文件类型,背后是vue-loader利用vue-style-loader和css-loader将<style>编译成JS代码,如果想引用CSS文件的话,那意味着增加了新的文件类型(*.css),也就是说需要安装并配置文件对应的loader才能被编译成JS代码。CSS文件对应的loader为css-loader,前面已经安装,只需在webpack.config.js中增加如下配置:
JS中引用CSS文件可以像引入JS模块一样调用require,比如在about.vue中添加require('../css/about.css');,那么about.css的样式就会被添加到页面中,自己可以做下测试。
同CSS,如果只在vue文件中使用,则不需要配置loader,实际项目中一般只会采用一种,下面将3中常用的一起测试一下。在about.vue添加测试代码如下:
运行效果如下:
如果想通过引用文件的方式来加载样式,就必须配置loader,配置如下:
将about.vue中各种语言<style>节点中的样式放进一个对应文件中,然后在about.vue中引用各个文件如下:
尝试着执行npm run build(我用的是cnpm),如图:
执行完后发现多了一个dist目录,里面只有一个build.js文件,新增的目录及文件是由webpack.config.js的output配置指定的。查看build.js,我们发现会发现几个问题:
就加了一个about.vue文件,怎么build.js就233k了,虽然是未压缩版,依旧很吓人,不禁让人怀疑是不是哪里出错了,心想后面加了很多页面岂不是得好几M了。
js怎么压缩呢?难道还得构建完后再使用别的插件来压缩js?这么折腾人可不好。
页面中添加的CSS哪里去了?怎么在build.js里面!我可不想把CSS放到js里面,怎么才能把CSS单独输出到一个文件里呢?
页面中引用图片哪里去了?又在build.js里面!怎么做到的,原来是转成DataUrl了,那要是一个几百k的图片,不敢想了……
以上这些问题使用webpack插件就迎刃而解了。
下面依次来介绍哪些插件可以解决上面的问题以及插件的使用方法。
warinings:false是在删除无用代码的时候,不显示警告,其他同类的配置可以查阅github,下面来执行看看效果。
虽然报错了,仍然输出了build.js,只是未压缩,上图错误是指在main.js第9行12列符号错误,也就是说不识别箭头函数,为啥运行可以?其实如果你去看运行的代码,会发现ES6的语法并未转换,只是Chrome支持这些语法,如果用import就会报错了。解决方法是增加babel-loader,将ES6语法进行转换,在package.json如下模块并安装:
接着配置webpack.config.js:
上文在配置babel-loader的时候还加了很多参数,这里为什么没加?经测试这种方式(babel-loader?presets[]=es2015)没有效果,为此我还在github上提了个问题,尤雨溪(vue的作者)回答是query只适用于babel-loader,不适用于vue-loader,应该使用.babelrc。也可以在package.json中配置,两种配置方式如下:
配置好后再执行命令就不会报错了,压缩后的js大小由原来的233k到现在的90k。
配置方式如下,原来的style-loader就要去掉了,毕竟我们的目标是将CSS提取出来而不是放在head中。
在上面的配置中,样式被提取到了style.css文件,在index.html中添加引用后就生效了。启动发现vue文件中<style>里的样式并未纳入到style.css中,而是继续嵌入式在head中,这是由于vue-loader默认用的是vue-style-loader,想要将vue文件中的样式也提取到文件中,需要在webpack.config添加vue的loaders的配置:
现在已经将所有的样式都提取到了一个文件中,但你会发现CSS并未压缩,看来ExtractTextPlugin的职责并不包含压缩。
首先安装PostCSS,webpack使用postcss-loader而不是postcss,在package.json中添加postcss-loader并安装:
我们的首要目标是压缩CSS,既然PostCSS是依赖插件来完成特定目标的,那么我们需要先找到压缩CSS的插件,查询插件的地址为https://github.com/postcss/postcss或http://postcss.parts/,最终发现插件cssnano可以完成我们的目标。配置PostCSS主要包括两点:
在webpack.config中注册PostCSS所需插件,vue要单独指定;
在CSS先关的loader中添加postcss,包括vue的loaders。
在package.json中添加cssnano并安装:
配置如下:
上面利用cssnano完成了压缩,其实cssnano不止是完成了压缩,还优化了你的CSS代码,比如丢弃重复的样式规则、压缩选择器、合并规则等等,更多特性可以去官网查看。就这一个插件,你可能就感受到了PostCSS的魅力,还有很多插件可以帮助你提升效率,比如autoprefixer可以自动添加浏览器前缀等,只要你想的到,就没有它做不到。
name:'images/[name].[ext]' 将符合test正则的图片都存在images目录下,[name].[ext]是文件名模板,更多占位符请参考file-loader文档
limit:10000 小于10kb的图片才会被转化成DataUrl,设为0并不意味着所有的图片都不被转换,如果想所有图片都不被转换,建议设为1
在上一章节中提到了文件名模板占位符,添加版本号就是利用占位符[hash],而且无论哪种方式都是利用这个占位符做配置。JS、CSS 及 Image 的配置如下:
这里很容易想到一个问题,就是打包后的CSS和JS文件名改变了,那意味着index.html中对应的引用地址也要跟着改变,但是现在关键是不知道文件名被改成啥了?(PS:虽然构建完成后显示了,如下图所示)即使知道新的文件名,每次都要手动去改index.html中引用地址也很不方便,希望最好能在打包完成后自动修改对应的引用地址,就像图片一样,CSS或页面中引用的图片自动改成了最新的文件名。下面将阐述如何通过生成首页来解决这个问题。
调用插件可以不传配置项,那么index.html会生成到output.path所指定的目录下,生成的页面会引用构建好的CSS和JS文件,但是不会包含<div id="app">。通过配置生成首页的模板(template),可以在模板中添加无法通过配置来完成的内容,还可以设置首页标题(title)、生成的目录及文件名(filename)、favicon图标(favicon)等等,所有配置参见项目github地址。
自动生成的首页只能用于发布,webpack-dev-server并不会访问生成后的首页,而且在我们开发的过程中也不需要配置诸如JS及CSS的压缩、添加版本号等,因为这些配置无疑会带来server启动变慢、占用资源等问题,解决这些问题最直接的办法就是将开发和生产得配置文件分开。
使用 webpack 的 DefinePlugin 来指定生产环境,以便在压缩时可以让 UglifyJS 自动删除代码块内的警告语句。
OccurrenceOrderPlugin 可以根据模块调用次数,给模块分配 ids,常被调用的 ids 分配更短的 id,使得 ids 可预测,降低文件大小。既然能减小打包文件体积,当然要用上。
之前写了一篇如何搭建 vue 1.x 工程的文章《vue vue-router webpack》(以下简称为“上文”),在写上文的时候,vue 2还处于beta版本,现在已经正式发布了,在其发布后不久,我就尝试着按上文的步骤来搭建 vue 2.x 的工程,结论是上文的步骤基本上没什么大的问题,只有部分细节需要调整。为了保证整体步骤的完整性,本文依旧从零开始,以一个新手视角,尽可能详尽且不遗漏的阐述每个步骤,同时又为了精简步骤,上文中的一些无关痛痒的话就省略了,对于没有变化的步骤就直接复制上文。
注意:2017-03-14 将 vue 升至 2.2.4,本文会和 vue 最新版本(仅限 2.x.x)保持一致,升级所带来的影响点会记录在升级记录章节下。
初始化工程
新建工程目录vue2practice,在目录下执行npm init来创建一个package.json,在package.json中先添加以下必备模块:"devDependencies": { "vue": "^2.1.10", "vue-loader": "^10.0.2", // vue 组件(*.vue)的webpack模块加载器 "vue-router": "^2.1.3", // vue 路由插件 "webpack": "^1.14.0", // 模块加载器兼打包工具 "webpack-dev-server": "^1.16.2" // 轻量的 node.js express 服务器,用于开发调试 }
新建目录结构如下,新增的目录及文件先空着,下面的步骤会说明添加什么内容。
vue2pratice |-- package.json |-- index.html // 启动页面 |-- webpack.config.js // webpack配置文件 |-- src |-- components // vue UI 组件目录 |-- views // vue页面组件目录 |-- main.js // 入口文件 |-- router.js // vue-router配置 |-- app.vue // 工程首页组件
配置webpack
webpack 默认读取 webpack.config.js,文件名不能随便改,其中 entry 是必须配置的。module.exports = { entry: './src/main.js', output: { path: './dist', publicPath: '/dist/', filename: 'build.js' } }
其中output的各配置项作用如下:
path: './dist' 打包后js、css、image等存放的目录;
publicPath: '/dist/' 可以不配置,如果不配置则取默认publicPath: '/',在实际项目中,静态资源一般集中放在一个文件夹下,比如static目录,那么这里就应该改成publicPath: '/static/',相应的 index.html 中引用的 JS 也要改成src="/static/build.js",publicPath 可以解释为最终发布的服务器上 build.js 所在的目录,其他静态资源也应当在这个目录下。
filename: 'build.js' 打包的js文件名,index.html 引用的 JS 要和这里保持一致。
配置 webpack-dev-server,只需在package.json添加以下启动命令即可。
"scripts": { "dev": "webpack-dev-server --inline --hot --open" }
各命令参数作用如下:
--inline 一共两种模式,默认为iframe模式,inline和iframe模式最明显的区别就是访问路径的不同,iframe模式的访问路径是http://localhost:8080/webpack-dev-server/,实际上iframe模式页面嵌入的<iframe>的地址还是http://localhost:8080/,那岂不是可以直接访问,不禁想问为啥不直接访问呢?因为无论是哪种模式,都是为了做到修改代码后能自动刷新,其中iframe模式是在修改代码后,重新加载iframe,而--inline是刷新浏览器,本质上都是重新全部加载一遍
--hot iframe不需要配置,配置了反而不能正常刷新了,所以只能配合--inline使用,作用是开启热替换,修改代码后,浏览器只会重新加载修改的组件代码,不会全部重新加载
--open 自动打开浏览器
配置了server,习惯性的测试下上述配置是否成功,确保后续步骤在一个成功的基石上进行,执行npm install,安装完后执行npm run dev,浏览器会自动打开http://localhost:8080/,能访问(可以在index.html添加内容来确认是否启动成功)则说明上面的配置没问题。
Vue
新建页面
在 views 目录下新建about.vue,不像传统的页面,没有html的结构,其实就是一个vue组件,但是承载着页面的功能,后面访问/about 的就是此文件的内容。<template> <div> 这是{{page}}页面 </div> </template> <script> module.exports = { data: function () { return { page: 'about' } } } </script>
配置路由
vue 2.0必须配套使用vue-router 2.0,新版变化还比较大,熟悉1.0的需要再仔细看下2.0文档。新版配置更为简单,不需要先实例化后再调用map来配置(router.map({'path':{component:''}})),改为直接在实例化时传入配置(new VueRouter({ routes: [{path: '', component: ''}]})),因此可以把传入的配置提取到router.js中,方便后续配置,外部调用方式为new VueRouter(require('./router')) 。router.js内容如下:module.exports = { routes: [ { path: '/about', component: require('./views/about.vue') } ] }
首页
首页(index.html)只需引入打包后的 js 文件(src和webpack.config的output 配置一致),#app是整个网站的挂载点,简单点说其实整个网站就是一个 vue 的实例,#app就是实例el属性值。<body> <divid="app"></div> <scriptsrc="dist/build.js"></script> </body>
接下来就要配置入口js,这个也是和vue 1.0区别比较大的地方,vue-router 1.0通过VueRouter.start(App,'#app')来初始化整个网站,这种方法在vue-router 2.0已经被废弃,因此需要我们来自己来完成new Vue()。main.js内容如下:
const Vue = require('vue'); const VueRouter = require('vue-router'); const App = require('./app.vue'); Vue.use(VueRouter); const router = new VueRouter(require('./router')) new Vue({ el: '#app', router: router, render: h => h(App) })
链接路由的不再使用<a v-link="{ path: '/about' }"></a>,改为组件的方式。app.vue内容如下:
<template> <div> <div> <router-linkto="/about">about</router-link> </div> <div> <router-view></router-view> </div> </div> </template>
配置loader
上面添加了 vue 文件,需要在webpack.config.js中添加vue对应的loader,才能将vue文件解析成可执行的代码。module: { loaders: [ { test: /\.vue$/, loader: 'vue' } ] }
还记得上文提到过,这时候启动会报错,提示一些依赖的loader未安装,果然:
不同于上文的是这次只报了一个错,下面会说明原因。在package.json添加:
{ "vue-template-compiler":"^2.1.10" }
安装后再次启动就成功了。
上文报错提示缺少很多vue相关的loader,难道新版不需要了?查看node_modules目录,发现vue-loader所依赖的模块都已经安装了(除了vue-template-compiler是自己添加的)。
原来是vue-loader中已经将所依赖的模块放到了其目录下的package.json中的dependencies,因此不需要自己去添加了,同时也发现vue-template-complier出现在peerDependencies下,这也解释了为什么第一次启动会报缺少它的原因。如图所示:
还有在app.vue文件中使用了ES6的语法(比如:const,箭头函数)也不会报缺少babel-loader,这也归功于vue-loader新加的vue-template-es2015-compiler。不过仅仅是部分语法,比如import等语法还是需要安装babel-loader。
支持添加CSS
此时如果在about.vue文件中添加CSS代码会提示错误Cannot find module "!!vue-style-loader!css-loader!......,这是因为CSS所依赖的loader未安装,前面vue-loader已经自动将其依赖的vue-style-loader安装好了,但是vue-style-loader依赖的css-loader并没有自动安装,需要我们自己安装。在package.json中添加:"css-loader":"^0.26.0"
安装后我们来验证一下,在about.vue中添加如下样式:
<styletype="text/css"> .about { color: #f00 } </style>
运行效果如图所示:
只在vue文件中使用嵌入式(<style>)来添加CSS代码,是不用在webpack.config中配置loader的,可以理解为没有增加新的文件类型,背后是vue-loader利用vue-style-loader和css-loader将<style>编译成JS代码,如果想引用CSS文件的话,那意味着增加了新的文件类型(*.css),也就是说需要安装并配置文件对应的loader才能被编译成JS代码。CSS文件对应的loader为css-loader,前面已经安装,只需在webpack.config.js中增加如下配置:
{ test: /\.css$/, loader: 'vue-style!css' }
JS中引用CSS文件可以像引入JS模块一样调用require,比如在about.vue中添加require('../css/about.css');,那么about.css的样式就会被添加到页面中,自己可以做下测试。
支持添加图片等静态资源
vue模板以及CSS中免不了使用图片,现在如果直接加,又会报找不到模块的错误,静态资源(图片、图标字体、音频、视频、svg文件等)对应的loader为url-loader,loader信息及配置如下:{ "url-loader":"^0.5.7", "file-loader":"^0.9.0" // url-loader依赖file-loader }
{ test: /\.(jpe?g|png|gif|svg|mp3)$/, loader: "url" }
支持CSS预处理语言
实际项目中大多会采用less、sass、stylus中的一种预处理语言来组织整个项目的CSS,因此需要添加这些预处理语言对应的loader,各个预处理语言的loader信息如下:{ "less": "^2.3.1", // less-loader依赖less "less-loader": "^2.2.3", "node-sass": "^3.4.2", // sass-loader依赖node-sass "sass-loader": "^4.0.2", "stylus": "^0.54.5", // stylus-loader依赖stylus "stylus-loader": "^2.4.0" }
同CSS,如果只在vue文件中使用,则不需要配置loader,实际项目中一般只会采用一种,下面将3中常用的一起测试一下。在about.vue添加测试代码如下:
<template> <divclass="about"> <divclass="test-less">测试less</div> <divclass="test-sass">测试sass</div> <divclass="test-stylus">测试stylus</div> </div> </template> <stylelang="less"> @color: #00f; .test-less { color: @color; } </style> <stylelang="sass"> $color: #0ff; .test-sass { color: $color; } </style> <stylelang="stylus"> color = #f00; .test-stylus { color: color; } </style>
运行效果如下:
如果想通过引用文件的方式来加载样式,就必须配置loader,配置如下:
{ test: /\.less$/, loader: "vue-style!css!less" }, { test: /\.scss/, loader: "vue-style!css!sass" }, { test: /\.styl/, loader: "vue-style!css!stylus" }
将about.vue中各种语言<style>节点中的样式放进一个对应文件中,然后在about.vue中引用各个文件如下:
require('../css/about.less'); require('../css/about.scss'); require('../css/about.styl');
打包发布
项目打包发布有多种选择,比如用gulp,grunt,这里就采用webpack来打包,webpack打包只需一个命令,即webpack --progress --colors,后面参数可以不要,参数是为了让编译的输出内容带有进度(--progress)和颜色(--colors)。在package.json添加如下命令:"scripts": { "dev": "webpack-dev-server --inline --hot --open", "build":"webpack --progress --colors" }
尝试着执行npm run build(我用的是cnpm),如图:
执行完后发现多了一个dist目录,里面只有一个build.js文件,新增的目录及文件是由webpack.config.js的output配置指定的。查看build.js,我们发现会发现几个问题:
就加了一个about.vue文件,怎么build.js就233k了,虽然是未压缩版,依旧很吓人,不禁让人怀疑是不是哪里出错了,心想后面加了很多页面岂不是得好几M了。
js怎么压缩呢?难道还得构建完后再使用别的插件来压缩js?这么折腾人可不好。
页面中添加的CSS哪里去了?怎么在build.js里面!我可不想把CSS放到js里面,怎么才能把CSS单独输出到一个文件里呢?
页面中引用图片哪里去了?又在build.js里面!怎么做到的,原来是转成DataUrl了,那要是一个几百k的图片,不敢想了……
以上这些问题使用webpack插件就迎刃而解了。
使用webpack插件
什么是webpack插件?这个就类似gulp需要安装所需要的插件一样,webpack不可能做到什么事都会。虽然webpack本身就依赖各种loader,但是有些是还是loader无法完成的,这个时候就需要插件来助一臂之力了。webpack 本身内置了一些常用的插件,还可以通过 npm 安装第三方插件。下面依次来介绍哪些插件可以解决上面的问题以及插件的使用方法。
如何使用插件
插件的使用一般是在 webpack 的配置信息 plugins 选项中指定。比如:var webpack = require('webpack'); module.exports = { plugins: [ new webpack.BannerPlugin('Hello webpack') ] }
压缩JS
压缩JS用的插件为webpack.optimize.UglifyJsPlugin,uglifyjs应该比较熟悉,使用方法如下:new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } })
warinings:false是在删除无用代码的时候,不显示警告,其他同类的配置可以查阅github,下面来执行看看效果。
虽然报错了,仍然输出了build.js,只是未压缩,上图错误是指在main.js第9行12列符号错误,也就是说不识别箭头函数,为啥运行可以?其实如果你去看运行的代码,会发现ES6的语法并未转换,只是Chrome支持这些语法,如果用import就会报错了。解决方法是增加babel-loader,将ES6语法进行转换,在package.json如下模块并安装:
{ "babel-loader": "^6.2.8", "babel-core": "^6.18.2", "babel-preset-es2015":"6.18.0" }
接着配置webpack.config.js:
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel' }
上文在配置babel-loader的时候还加了很多参数,这里为什么没加?经测试这种方式(babel-loader?presets[]=es2015)没有效果,为此我还在github上提了个问题,尤雨溪(vue的作者)回答是query只适用于babel-loader,不适用于vue-loader,应该使用.babelrc。也可以在package.json中配置,两种配置方式如下:
配置好后再执行命令就不会报错了,压缩后的js大小由原来的233k到现在的90k。
提取CSS
提取CSS的插件叫extract-text-webpack-plugin,这个不是webpack自带的,需要自己安装,在package.json中添加:{ "extract-text-webpack-plugin": "^1.0.1" }
配置方式如下,原来的style-loader就要去掉了,毕竟我们的目标是将CSS提取出来而不是放在head中。
var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { module: { loaders: [{ test: /\.css$/, loader: ExtractTextPlugin.extract("css") }, { test: /\.less$/, loader: ExtractTextPlugin.extract("css!less") }, { test: /\.scss/, loader: ExtractTextPlugin.extract("css!sass") }, { test: /\.styl/, loader: ExtractTextPlugin.extract("css!stylus") }, ] }, plugins: [ new ExtractTextPlugin("css/style.css") ] }
在上面的配置中,样式被提取到了style.css文件,在index.html中添加引用后就生效了。启动发现vue文件中<style>里的样式并未纳入到style.css中,而是继续嵌入式在head中,这是由于vue-loader默认用的是vue-style-loader,想要将vue文件中的样式也提取到文件中,需要在webpack.config添加vue的loaders的配置:
module.exports = { module: {}, plugins: [], vue: { loaders: { css: ExtractTextPlugin.extract("css"), less: ExtractTextPlugin.extract("css!less"), scss: ExtractTextPlugin.extract("css!sass"), stylus: ExtractTextPlugin.extract("css!stylus") } } }
现在已经将所有的样式都提取到了一个文件中,但你会发现CSS并未压缩,看来ExtractTextPlugin的职责并不包含压缩。
PostCSS
既然上面提到CSS并未压缩,那只能去寻找其他的插件来完成这个工作,PostCSS无疑就是最佳的选择之一,当然PostCSS并不是为压缩CSS而生的,它是一个使用JS插件来转换CSS的工具, 这些插件可以支持使用变量,混入,转换未来的CSS语法,内联图片等操作。首先安装PostCSS,webpack使用postcss-loader而不是postcss,在package.json中添加postcss-loader并安装:
"postcss-loader":"^1.2.0"
我们的首要目标是压缩CSS,既然PostCSS是依赖插件来完成特定目标的,那么我们需要先找到压缩CSS的插件,查询插件的地址为https://github.com/postcss/postcss或http://postcss.parts/,最终发现插件cssnano可以完成我们的目标。配置PostCSS主要包括两点:
在webpack.config中注册PostCSS所需插件,vue要单独指定;
在CSS先关的loader中添加postcss,包括vue的loaders。
在package.json中添加cssnano并安装:
"cssnano":"^3.8.1"
配置如下:
module.exports = { module: { loaders: [ { test: /\.css$/, loader: ExtractTextPlugin.extract("css!postcss") }, { test: /\.less$/, loader: ExtractTextPlugin.extract("css!postcss!less") }, { test: /\.scss/, loader: ExtractTextPlugin.extract("css!postcss!sass") }, { test: /\.styl/, loader: ExtractTextPlugin.extract("css!postcss!stylus") } ] }, vue: { loaders: { css: ExtractTextPlugin.extract("css!postcss"), less: ExtractTextPlugin.extract("css!postcss!less"), scss: ExtractTextPlugin.extract("css!postcss!sass"), stylus: ExtractTextPlugin.extract("css!postcss!stylus") }, postcss: [require("cssnano")] }, postcss: [require("cssnano")] }
上面利用cssnano完成了压缩,其实cssnano不止是完成了压缩,还优化了你的CSS代码,比如丢弃重复的样式规则、压缩选择器、合并规则等等,更多特性可以去官网查看。就这一个插件,你可能就感受到了PostCSS的魅力,还有很多插件可以帮助你提升效率,比如autoprefixer可以自动添加浏览器前缀等,只要你想的到,就没有它做不到。
提取图片
如果是很小的图片,转成DataUrl放在JS中还是可以接受的,毕竟可以减少请求,大图肯定不适合这种方式。如果你完全不想这么做,或者只想单独把大图提出来,只需通过增加url-loader的参数即可。{ test: /\.(jpe?g|png|gif|svg)$/, loader: "url", query:{ name:'images/[name].[ext]', limit:10000 // 单位:字节 } }
name:'images/[name].[ext]' 将符合test正则的图片都存在images目录下,[name].[ext]是文件名模板,更多占位符请参考file-loader文档
limit:10000 小于10kb的图片才会被转化成DataUrl,设为0并不意味着所有的图片都不被转换,如果想所有图片都不被转换,建议设为1
静态资源添加版本号
对于前后端分离的站点来讲,前端所有资源都是静态的,因此防止浏览器缓存就非常有必要。防止缓存的方法一般有两种,一种是在文件名中添加文件内容的hash值(build.xxxx.js),另外一种方法是给每个http请求加版本号参数(build.js?xxxx)。下面分别说明下两种方法如何配置。在上一章节中提到了文件名模板占位符,添加版本号就是利用占位符[hash],而且无论哪种方式都是利用这个占位符做配置。JS、CSS 及 Image 的配置如下:
// hash可配置选项 [<hashType>:hash:<digestType>:<length>] 即 [哈希算法类型:hash:哈希摘要类型:长度] // JS filename: 'build.[hash:8].js' // hash filename: 'build.js?[hash:8]' // query // CSS new ExtractTextPlugin("css/style.[hash:8].css") //hash new ExtractTextPlugin("css/style.css?[hash:8]") //query // Image name: 'images/[name][hash:8].[ext]' // hash name: 'images/[name].[ext]?[hash:8]' // query
这里很容易想到一个问题,就是打包后的CSS和JS文件名改变了,那意味着index.html中对应的引用地址也要跟着改变,但是现在关键是不知道文件名被改成啥了?(PS:虽然构建完成后显示了,如下图所示)即使知道新的文件名,每次都要手动去改index.html中引用地址也很不方便,希望最好能在打包完成后自动修改对应的引用地址,就像图片一样,CSS或页面中引用的图片自动改成了最新的文件名。下面将阐述如何通过生成首页来解决这个问题。
自动生成首页
自动生成首页需要安装webpack插件html-webpack-plugin,模块信息如下:"html-webpack-plugin":"^2.24.1"
调用插件可以不传配置项,那么index.html会生成到output.path所指定的目录下,生成的页面会引用构建好的CSS和JS文件,但是不会包含<div id="app">。通过配置生成首页的模板(template),可以在模板中添加无法通过配置来完成的内容,还可以设置首页标题(title)、生成的目录及文件名(filename)、favicon图标(favicon)等等,所有配置参见项目github地址。
// 使用默认配置 new HtmlWebpackPlugin() // 自定义配置 new HtmlWebpackPlugin({ title:'首页标题', filename:'../index.html', template:'index.tpl.html', favicon:'src/images/favicon.ico' ... })
自动生成的首页只能用于发布,webpack-dev-server并不会访问生成后的首页,而且在我们开发的过程中也不需要配置诸如JS及CSS的压缩、添加版本号等,因为这些配置无疑会带来server启动变慢、占用资源等问题,解决这些问题最直接的办法就是将开发和生产得配置文件分开。
其他插件
除了上述插件外,还有一些打包时需要用到的插件,列举如下。使用 webpack 的 DefinePlugin 来指定生产环境,以便在压缩时可以让 UglifyJS 自动删除代码块内的警告语句。
new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } })
OccurrenceOrderPlugin 可以根据模块调用次数,给模块分配 ids,常被调用的 ids 分配更短的 id,使得 ids 可预测,降低文件大小。既然能减小打包文件体积,当然要用上。
new webpack.optimize.OccurrenceOrderPlugin()
后续
后面可以做的事还有很多,鉴于本文到这里已经很长了,于是决定将后续的配置另起一篇文章(《vue2 vue-router2 webpack(续)》)来讲解。升级记录
vue 2.2.4
2017-03-14 将 vue 升级至 2.2.4,步骤无影响,所有升级模块信息如下:"vue": "^2.2.4", "vue-loader": "^11.1.4", "vue-router": "^2.3.0", "vue-template-compiler": "^2.2.4", "webpack-dev-server": "^1.16.3"
vue 2.1.10
2017-01-21将vue升级至2.1.10,无影响点。所有升级模块信息如下:"vue": "^2.1.10", // 原2.1.9 "vue-template-compiler": "^2.1.10", // 原2.1.9 "vue-router": "^2.1.3", // 原2.1.1
vue 2.1.9
2017-01-17将vue升级至2.1.9,无影响点。所有升级模块信息如下:"vue": "^2.1.9", // 原2.1.8 "vue-template-compiler": "^2.1.9" // 原2.1.8
vue 2.1.8
2017-01-10将vue升级至2.1.8,无影响点。所有升级模块信息如下:"vue": "^2.1.8", // 原2.1.6 "vue-template-compiler": "^2.1.8" // 原2.1.6
vue 2.1.6
2016-12-20 将vue 升级至 2.1.6,无影响点。所有升级模块信息如下:"vue": "^2.1.6", // 原 2.1.4 "vue-template-compiler": "^2.1.6", // 原 2.1.4 "webpack": "^1.14.0", // 原 1.13.3
vue 2.1.4
2016-12-07 将 vue 升级至 2.1.4,无影响点。所有升级模块信息如下:"vue-router": "^2.1.1", // 原2.1.0 "vue-template-compiler": "^2.1.4" // 原2.1.3
vue 2.1.3
2016-12-01 将 vue 升级至 2.1.3,其他模块并未升级,升级后需要安装vue-template-compiler项目才能跑起来相关文章推荐
- 使用vue2、vuex、vue-router、axios等重写饿了么点餐系统
- 详解从零搭建 vue2 vue-router2 webpack3 工程
- vue2 + router + vuex + vux + axios 开发的一点总计
- vue2 vue-router 组装
- 使用vue2、vuex、vue-router、axios等重写饿了么点餐系统
- vue2笔记 ― vue-router路由懒加载的实现
- 从零构建 vue2 + vue-router + vuex 开发环境到入门,实现基本的登录退出功能
- Vue2项目架构搭建(八)—— vue-router2路由配置和调用
- 搭建vue2 vue-router2 webpack项目
- 基于vue2 + vue-router + vuex 构建的一个新闻类大型单页面应用 —— 今日头条
- VUE 全家桶,vue2-vue-router-vuex-axios - 一个看笑话的 webapp
- VUE 全家桶,vue2-vue-router-vuex-axios - 一个看笑话的 webapp
- 基于vue2 + vue-router + vuex 构建的一个音乐类单页面应用 —— echo回声
- 利用vue+vue-router+elementUI实现简易通讯录
- 基于Vue、Vuex、Vue-router实现的购物商城(原生切换动画)效果
- vue-router设置页面标题
- 使用Vue-Router 2实现路由功能
- vue-router 路由 iframe嵌套
- 实例讲解Vue.js中router传参
- vue-router中参数传递 && 编程式导航 && 坑 && beforeRouteEnter