目录
- 1 简单的使用webpack进行打包
- 2 配置module
- 3 webpack打包其他资源使用的处理器
- 4 webpack-dev-server配置与使用
- 5 render与template使用的vue文件有所不同
- 6 配置vue的jsx写法以及postcss
- 7 css单独分离打包
- 8 webpack区分打包类库代码及hash优化
- 9 项目webpack整理
- 10 vue-loader配置
- 11 打包前清空打包输出的目录
- 12 vue-loader的cssModule配置
- 13 安装eslint规范代码写作,editorconfig统一编辑器规范,precommit代码不规范不许git提交
- 14 最后
简单的使用webpack进行打包
创建一个目录作为项目目录,执行命令:
npm init
进行项目初始化,这个过程会填写一些项目的基本信息,有需要就填写,没有的话,就一直回车默认,执完毕,会生成名为package.json的文件。
安装webpack
执行命令:
npm install webpack
来安装webpack。
如果是webpack4还需要安装webpack-cli:npm install webpack-cli
安装vue
执行命令:
npm install vue
安装vue模块。
在项目根目录新建文件:index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Webpack4 && Vue</title> </head> <body> <div id="root"><!--vue渲染会挂载到这里,根据id="root"--> </div> </body> </html> |
然后,在项目根目录下创建src文件夹,作为源码放置的目录。在src目录下创建App.vue文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<template> <!--代表了网页的html部分--> </template> <script> /*代表了网页的javascript部分,并且可以控制template*/ export default { name: "App.vue" } </script> <style scoped> /*代表了网页的css部分,并且可以控制template*/ </style> |
这几个部分组成了vue的基本结构。
我们在App.vue里写一些简单的vue语法代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<template> <div id="app"> {{text}} </div> </template> <script> export default { name: "App.vue",//当前vue的名字 data(){//数据控制 return { text:'abc'//与template的text进行数据的绑定 } } } </script> <style scoped> /*template里id="app"*/ #app{ color: red; } </style> |
这只是一个vue组件,浏览器只会处理普通的html文件,所以要把vue转换成普通的html文件结构。这时,可以创建vue入口文件,在src目录里创建main.js作为入口文件:
1 2 3 4 5 6 7 8 |
import Vue from 'vue'//(1)引入vue模块 import App from './App.vue'//(3)引入根组件 //(2)实例vue,并配置vue new Vue({//这个大括号里是vue的配置项 el:"#root",//把处理过后的vue文件挂载到HTML的那个节点上,这里挂载到html的id="root"的节点上 render:(h)=>h(App)//渲染,所有的vue都渲染到哪里,这里是把vue渲染到名为App.vue的根组件上 }); |
挂载的第二种方法
除了使用vue的el属性指明挂载点,还可以使用$mount()来完成挂载,效果一样:
1 2 3 4 5 6 |
import Vue from 'vue' import App from './App.vue' new Vue({ render:(h)=>h(App) }).$mount("#root");//挂载到id="root"节点上 |
简单配置webpack
简单的vue写好了,那么现在使用webpack来打包处理vue,在这之前,需要配置一下webpack,告诉webpack的处理规则。在项目根目录创建名为webpack.config.js的文件作为webpack的配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
const path = require('path');//路径处理模块 module.exports = { //context: path.join(__dirname,'../'),//是 webpack 编译时的基础目录,入口起点(entry)会相对于此目录查找。若不配置,默认值为当前目录 //入口,webpack从这里开始处理打包。这是因为index.js是vue的入口文件 entry:{ app:path.join(__dirname,'./src/main.js') }, //出口,webpack打包后怎么处理 output: { filename:'[name].[hash:8].js',//将所有依赖的模块合并输出到一个js文件内 path: path.join(__dirname,'./dist')//路径 }, //模块,webpack处理所需要的模块 module: { //规则,模块的使用规则 rules: [//test:是正则,loader:是使用哪种加载器处理 {//.vue的文件,都使用vue-loader处理 test:/.vue$/, loader:'vue-loader' } ] } } |
为什么会有filename上的格式
这是es6提供的一种语法。
[name]:取得文件名;
[hash:8]:取得hash值,长度截取为8个字符;
此外还有一些额外的模板符号:
- [hash] 模块标识符(module identifier)的 hash
- [chunkhash] chunk 内容的 hash
- [name] 模块名称
- [id] 模块标识符(module identifier)
- [query] 模块的 query,例如,文件名 ? 后面的字符串
vue是运行时依赖,webpack需要合适loader将vue文件解释为webpack可以理解的格式用于构建,所以我们需要vue-loader来转换vue单文件。
使用npm安装vue-loader:
npm install vue-loader
vue单文件中分为三个部分,其中template部分需要专用的插件进行转换。
安装vue-template-compiler:
npm install vue-template-compiler
这个使用vue-loader自动调用的插件,也是官方默认的,不需要任何配置。
如果你不使用他,打包的时候会报错。
简单来说他的功能是将template部分的模板转为render函数。
然后我们需要处理css,vue-loader需要css-loader才可以运行。同时也需要安装:vue-style-loader。
安装css-loader、vue-style-loader:
npm install css-loader vue-style-loader
css-loader的作用仅仅是将css转为webpack可以解释的类型,如果我们需要将样式使用起来插入到html中,必须使用额外的插件。
插件的安装
vue-loader
VueLoaderPlugin在vue-loaderv15的版本中,这个插件是必须启用的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin');//引入vue-loader的插件 module.exports = { entry:{ app:path.join(__dirname,'./src/main.js') }, output:{ filename:'[name].[hash:8].js', path:path.join(__dirname,'./dist') }, module: { rules: [ { test: /\.vue$/, use: ['vue-loader'] }, { test: /\.css$/, use: ['vue-style-loader','css-loader'] } ] }, plugins:[ new VueLoaderPlugin()//开启插件 ] } |
html-webpack-plugin
这个插件可以自动生成一个html文件,并且将资源文件按照正确顺序插入到html中。
安装html-webpack-plugin:
npm install html-webpack-plugin
webpack.config.js添加配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');//引入 module.exports = { entry:{ app:path.join(__dirname,'./src/main.js') }, output:{ filename:'[name].[hash:8].js', path:path.join(__dirname,'./dist') }, module: { rules: [ { test: /\.vue$/, use: ['vue-loader'] }, { test: /\.css$/, use: ['vue-style-loader','css-loader'] } ] }, plugins:[ new HtmlWebpackPlugin({ template:'index.html',// 指定模板html文件 filename:'index.html'// 输出的html文件名称 }), new VueLoaderPlugin() ] } |
clean-webpack-plugin
当你多次打包的时候,你会发现由于使用hash来命名输出的文件每次的文件名称都不一样,导致文件越来越多。
使用CleanWebpackPlugin可以每次构建前清空输出目录。
安装:
npm install clean-webpack-plugin
编辑webpack.config.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require("clean-webpack-plugin");//引入 module.exports = { entry:{ app:path.join(__dirname,'./src/main.js') }, output:{ filename:'[name].[hash:8].js', path:path.join(__dirname,'./dist') }, module: { rules: [ { test: /\.vue$/, use: ['vue-loader'] }, { test: /\.css$/, use: ['vue-style-loader','css-loader'] } ] }, plugins:[ // 注意插件的加载顺序 new CleanWebpackPlugin(['dist']),// 清空指定文件夹 new HtmlWebpackPlugin({ template:'index.html', filename:'index.html' }), new VueLoaderPlugin() ] } |
创建项目webpack打包命令
可以使用全局的webpack命令来打包,但是不推荐,会造成混乱。打开package.json,在”scripts”:{}里创建webpack打包命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
{ "name": "muke_vuewebpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack --config webpack.config.js" }, "author": "", "license": "ISC", "devDependencies": { "css-loader": "^0.28.7", "vue": "^2.5.13", "vue-loader": "^13.6.0", "vue-template-compiler": "^2.5.13", "webpack": "^3.10.0" } } |
"build": "webpack --config webpack.config.js"
“build”:是一个键名;
“webpack –config webpack.config.js”:webpack --config
使用指定的webpack配置文件来进行打包操作,webpack.config.js
是我们写的webpack配置文件。
打包项目
在命令终端,执行命令:
npm run build
即可webpack打包项目,成功后会在根目录生成的一个名为dist的文件夹,里面是打包好的文件。
配置module
配置 Loader
rules
配置模块的读取和解析规则,通常用来配置 Loader。其类型是一个数组,数组里每一项都描述了如何去处理部分文件。 配置一项 rules
时大致通过以下方式:
- 条件匹配:通过 test 、 include 、 exclude 三个配置项来命中 Loader 要应用规则的文件。
- 应用规则:对选中后的文件通过
use
配置项来应用 Loader,可以只应用一个 Loader 或者按照从后往前的顺序应用一组 Loader,同时还可以分别给 Loader 传入参数。 - 重置顺序:一组 Loader 的执行顺序默认是从右到左执行,通过
enforce
选项可以让其中一个 Loader 的执行顺序放到最前或者最后。
下面来通过一个例子来说明具体使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
module: { rules: [ { // 命中 JavaScript 文件 test: /\.js$/, // 用 babel-loader 转换 JavaScript 文件 // ?cacheDirectory 表示传给 babel-loader 的参数,用于缓存 babel 编译结果加快重新编译速度 use: ['babel-loader?cacheDirectory'], // 只命中src目录里的js文件,加快 Webpack 搜索速度 include: path.resolve(__dirname, 'src') }, { // 命中 SCSS 文件 test: /\.scss$/, // 使用一组 Loader 去处理 SCSS 文件。 // 处理顺序为从后到前,即先交给 sass-loader 处理,再把结果交给 css-loader 最后再给 style-loader。 use: ['style-loader', 'css-loader', 'sass-loader'], // 排除 node_modules 目录下的文件 exclude: path.resolve(__dirname, 'node_modules'), }, { // 对非文本文件采用 file-loader 加载 test: /\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/, use: ['file-loader'], }, ] } |
在 Loader 需要传入很多参数时,你还可以通过一个 Object 来描述,例如在上面的 babel-loader 配置中有如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 |
use: [ { loader:'babel-loader', options:{ cacheDirectory:true, }, // enforce:'post' 的含义是把该 Loader 的执行顺序放到最后 // enforce 的值还可以是 pre,代表把 Loader 的执行顺序放到最前面 enforce:'post' }, // 省略其它 Loader ] |
上面的例子中 test include exclude 这三个命中文件的配置项只传入了一个字符串或正则,其实它们还都支持数组类型,使用如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ test:[ /\.jsx?$/, /\.tsx?$/ ], include:[ path.resolve(__dirname, 'src'), path.resolve(__dirname, 'tests'), ], exclude:[ path.resolve(__dirname, 'node_modules'), path.resolve(__dirname, 'bower_modules'), ] } |
数组里的每项之间是或的关系,即文件路径符合数组中的任何一个条件就会被命中。
noParse
noParse
配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析和处理,这样做的好处是能提高构建性能。 原因是一些库例如 jQuery 、ChartJS 它们庞大又没有采用模块化标准,让 Webpack 去解析这些文件耗时又没有意义。
noParse
是可选配置项,类型需要是 RegExp
、[RegExp]
、function
其中一个。
例如想要忽略掉 jQuery 、ChartJS,可以使用如下代码:
1 2 3 4 5 6 7 8 9 |
// 使用正则表达式 noParse: /jquery|chartjs/ // 使用函数,从 Webpack 3.0.0 开始支持 noParse: (content)=> { // content 代表一个模块的文件路径 // 返回 true or false return /jquery|chartjs/.test(content); } |
注意被忽略掉的文件里不应该包含 import
、 require
、 define
等模块化语句,不然会导致构建出的代码中包含无法在浏览器环境下执行的模块化语句。
parser
因为 Webpack 是以模块化的 JavaScript 文件为入口,所以内置了对模块化 JavaScript 的解析功能,支持 AMD、CommonJS、SystemJS、ES6。 parser
属性可以更细粒度的配置哪些模块语法要解析哪些不解析,和 noParse
配置项的区别在于 parser
可以精确到语法层面, 而 noParse 只能控制哪些文件不被解析。 parser
使用如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
module: { rules: [ { test: /\.js$/, use: ['babel-loader'], parser: { amd: false, // 禁用 AMD commonjs: false, // 禁用 CommonJS system: false, // 禁用 SystemJS harmony: false, // 禁用 ES6 import/export requireInclude: false, // 禁用 require.include requireEnsure: false, // 禁用 require.ensure requireContext: false, // 禁用 require.context browserify: false, // 禁用 browserify requireJs: false, // 禁用 requirejs } }, ] } |
webpack打包其他资源使用的处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
//模块,webpack处理所需要的模块 module: { //规则,模块的使用规则 rules: [//test:是正则,loader:是使用哪种加载器处理 {//.vue的文件,都使用vue-loader处理 test:/\.vue$/, loader:'vue-loader' }, {//.css的文件,使用style-loader、css-loader处理 test:/\.css$/, use:[ 'style-loader', 'css-loader'//css-loader处理完,往上抛给style-loader处理 ] }, {//.styl, css预编译器,使用style-loader、css-loader、stylus-loader递归处理 test:/\.styl$/, use:[ 'style-loader', 'css-loader',//css-loader处理完,往上抛给style-loader处理 'stylus-loader'//stylus-loader处理完,往上抛给css-loader处理 ] }, {//图片处理 test:/\.(jpg|jpeg|png|gif|svg)$/, use:[ { loader: "url-loader", options:{//loader选项配置 limit:1024, name: '[name]-aaa.[ext]' } } ] } ] } |
webpack的resolve属性使用
1 2 3 4 5 6 7 8 9 |
resolve: { //组内填入什么后缀,引入该后缀时可以文件名可以不带后缀 extensions: ['.vue','.js','.json'], //设置别名 alias:{ '@': path.join(__dirname,'../src'),//@ 等于 设定的路径 'vue': 'vue/dist/vue.js' } }, |
图片、多媒体以及字体处理
安装url-loader:npm install url-loader
安装file-loader:npm install file-loader
配置webpack
配置图片处理模块规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
module: { rules: [ { test: /\.vue$/, use: ['vue-loader'] }, { test:/\.css$/, use: ["vue-style-loader","css-loader"] }, {// 小于8K的图片将直接以base64的形式内联在代码中,可以减少一次http请求。 // 大于8k的呢?则直接file-loader打包, 这里并没有写明file-loader。 // 但是确实是需要安装,否则会有问题。而name也是file-loader的属性。 // 重复一次 必须安装file-loader test:/\.(jp?g|png|gif|svg)(\?.*)?$/, use:[ { loader: "url-loader", options:{//loader选项配置 // 图片大小限制 单位b limit:8192, // 生成的文件的存放目录与文件的命名规则 name: 'img/[name].[hash:8].[ext]' } } ] }, {//多媒体的处理 test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'media/[name].[hash:7].[ext]' } }, {//字体的处理 test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'fonts/[name].[hash:7].[ext]' } } ] } |
sass
在App组件写下一个sass语法,要在style标签标明语言,lang="scss"
,不是sass,这个要注意:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<template> <div id="app"> <span class="font">{{text}}</span> </div> </template> <script> export default { name: "App", data(){ return { text:"这是APP组件" } } } </script> <style lang="scss" scoped> $font-color: #F90; .font { $width: 100px; width: $width; color: $font-color; } </style> |
配置webpack
安装sass-loader:npm install sass-loader
安装node-sass:npm install node-sass
配置sass模块规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
module: { rules: [ { test: /\.vue$/, use: ['vue-loader'] }, { test:/\.css$/, use: ["vue-style-loader","css-loader"] }, { test:/\.scss$/,//匹配的文件类型 use: [ {//loader处理的顺序是该数组中从右到左的顺序 loader: 'style-loader'//将 JS 字符串生成为 style 节点 },{ loader: 'css-loader'//将 CSS 转化成 CommonJS 模块 },{ loader: 'sass-loader'//将sass编译成css } ] } ] } |
webpack-dev-server配置与使用
执行命令:npm i webpack-dev-server
安装webpack-dev-server。
webpack.config.js配置webpack-dev-server
webpack-dev-server的配置需要对原来的webpack.config.js的结构做一点小改动,并且配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const config = {//把webpack的配置作为一个对象 mode: "development", entry:{ app:path.join(__dirname,'./src/main.js') }, output:{ filename:'[name].[hash:8].js', path:path.join(__dirname,'./dist') }, module: { rules: [ { test:/\.vue$/, loader:"vue-loader" }, { test:/\.css$/, use: ["vue-style-loader","css-loader"] }, { test:/\.(jpg|jpeg|png|gif|svg)$/, use:[ { loader: "url-loader", options:{ limit:1024, filename:"[name].[chunkhash:8].[ext]" } } ] } ] }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ template: 'index.html', filename: 'index.html' }), new VueLoaderPlugin(), ] } //webpack-dev-server的使用,是在webpack的配置对象中使用devServer属性,里面可以定义webpack-dev-server的一些属性 config.devServer = { //这里是webpack-dev-server的配置 port: '8000', host: '0.0.0.0', open:true, overlay:{ errors:true }, hot:true, inline:true } module.exports = config;//最后把webpack的配置向外提供 |
package.json中使用webpack-dev-server命令
打开package.json,编辑脚本命令,使用webpack-dev-server。因为一般是在开发环境使用到webpack-dev-server,所以只在开发环境使用webpack-dev-server:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
{ "name": "muke_vuewebpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack --config webpack.config.js", "dev": "webpack-dev-server --config webpack.config.js" }, "author": "", "license": "ISC", "devDependencies": { "css-loader": "^0.28.7", "file-loader": "^1.1.6", "style-loader": "^0.19.1", "stylus": "^0.54.5", "stylus-loader": "^3.0.1", "url-loader": "^0.6.2", "vue": "^2.5.13", "vue-loader": "^13.6.0", "vue-template-compiler": "^2.5.13", "webpack": "^3.10.0" }, "dependencies": { "webpack-dev-server": "^2.9.7" } } |
标记webpack配置文件属于什么环境
打开webpack.config.js,加上配置mode: "development"
,这个属性是webpack4的,webpack3没有:
1 2 3 4 5 6 7 8 |
module.export = { mode: "development",//webpack-dev-server需要配置一下mode,标记当前是什么环境 entry:path.join(__dirname,'src/index'), output: { filename: "[name].[hash:8].js", path: path.join(__dirname,'dist') } } |
cross-env
cross-env一般用于webpack3,webpack4用不到。webpack-dev-server需要区别不同的开发环境来读取配置文件,这就要借助cross-env来帮助webpack-dev-server完成这个事情。执行命令:npm install cross-env
安装cross-env。
打开package.json 使用cross-env:
1 2 3 4 |
"scripts": { "build": "cross-env NODE_ENV=production webpack --config webpack.config.js", "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js" }, |
NODE_ENV=
指定当前的环境,这个值会存储到node.js的全局变量中,可以通过process.env.NODE_ENV来获取到这个值。
回到webpack.config.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
const path = require('path'); const isDev = process.env.NODE_ENV == 'development';//判断当前是否为开发环境 const config = {//作为一个配置项 target: "web", entry:path.join(__dirname,'./src/main'), output: { filename: "[name].[hash:8].js", path: path.join(__dirname,'./dist') }, module: { rules: [ { test:/\.vue$/, loader:'vue-loader' } ] } } if (isDev){ //如果是开发环境,配置对应的服务器 config.devServer = { port: '8000',//端口 host: '0.0.0.0',//地址,0.0.0.0可以不限IP来访问 overlay:{//有任何的错误,都可以显示到网页上 errors:true } } } module.exports = config; |
webpack默认插件
webpack它自己也有个插件,一般是默认使用,如果需要定制这个插件,则需要先引入webpack。
webpack.config.js添加配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
const path = require('path'); const HTMLPlugin = require('html-webpack-plugin'); const webpack = require('webpack');//引入webpack const isDev = process.env.NODE_ENV == 'development'; const config = { target: "web", entry:path.join(__dirname,'src/index'), output: { filename: "bundle.js", path: path.join(__dirname,'dist') }, module: { rules: [ { test:/\.vue$/, loader:'vue-loader' } ] }, //插件,webpack使用的插件 plugins: [ //webpack默认插件配置 new webpack.DefinePlugin({ "process.env":{ NODE_ENV:isDev?'"development"':'production' } }), new HTMLPlugin() ] } if (isDev){ config.devServer = { port: '8000', host: '0.0.0.0', overlay:{ errors:true } } } module.exports = config; |
在控制台执行命令:
npm run dev
就会打包项目并生成一个服务器,访问服务器地址就可以浏览到内容。
webpack-dev-server的其它配置项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
devServer: { contentBase: "./",//本地服务器所加载的页面所在的目录 historyApiFallback: true,//这个配置属性是用来应对返回404页面时定向到特定页面用的 host:'0.0.0.0',//设置服务器的主机号,默认是localhost,推荐'0.0.0.0' port:7000,//设置端口号 inline: true,//自动刷新 hot:true,//热模块替换机制 compress:true,//gzip压缩 overlay: true, //用于在浏览器输出编译错误 stats: "errors-only" ,//用来控制编译的时候shell上的输出内容,因为我们并不需要所有的内容,而只是需要部分的如errors等 open:true, //当open选项被设置为true时,dev server将直接打开浏览器 proxy: {//重定向,是解决跨域的好办法 "/api": { target: "http://localhost:3000", pathRewrite: {"^/api" : ""} } }, publicPath: "/assets/"//设置编译后文件的路径 } |
1、contentBase
用于告诉服务器文件的根目录。这主要用来需要引用静态文件的时候。devServer.publicPath被用来规定变异文件的路径地址,在下面将详细介绍。
默认情况下就是当前工作的文件夹地址,但是可以修改为其他的地址
contentBase: path.join(__dirname, "public")
2、historyApiFallback
这个配置属性是用来应对返回404页面时定向到特定页面用的。
语法是向historyApiFallback对象中的rewrites属性传一个对象格式,如下:
1 2 3 4 5 |
historyApiFallback:{ rewrites:[ {from:/./,to:'/404.html'} ] } |
3、host
设置服务器的主机号,默认是localhost,但是可以自己进行设置,如:
host: "0.0.0.0"
此时,localhost:7000和0.0.0.0:7000都能访问成功
4、port
设置端口号,如下面的7000
5、hot 和 inline
自动刷新和模块热替换机制
5.1 hot
热模块替换机制
hot: true
注意,如果你的项目中使用了热模块替换机制,HotModuleReplacementPlugin插件会自动添加到项目中,而不需要再在配置文件中做添加。
5.2 inline
webpack-dev-server有两种模式可以实现自动刷新和模块热替换机制
1. Iframe mode(默认,无需配置)
页面被嵌入在一个iframe里面,并且在模块变化的时候重载页面
2.inline mode(需配置)添加到bundle.js中
当刷新页面的时候,一个小型的客户端被添加到webpack.config.js的入口文件中
6、compress
这是一个布尔型的值,当它被设置为true的时候对所有的服务器资源采用gzip压缩
采用gzip压缩的优点和缺点:
优点:对JS,CSS资源的压缩率很高,可以极大得提高文件传输的速率,从而提升web性能
缺点:服务端要对文件进行压缩,而客户端要进行解压,增加了两边的负载
7、overlay
用于在浏览器输出编译错误的,默认是关闭的,需要手动打开:
overlay: true
如果你想将warnings一同打印出来,可设置:
1 2 3 4 |
overlay: { warnings: true, errors: true } |
8、stats
这个配置属性用来控制编译的时候shell上的输出内容,因为我们并不需要所有的内容,而只是需要部分的如errors等
在shell中只输出errors:
stats: "errors-only"
9、open
当open选项被设置为true时,dev server将直接打开浏览器
10、proxy
重定向是解决跨域的好办法,当后端的接口拥有独立的API,而前端想在同一个domain下访问接口的时候,可以通过设置proxy实现。
如果后端接口地址是10.10.10.10:3000,你可以这样设置:
1 2 3 |
proxy: { "/api": "http://10.10.10.10:3000" } |
一个 “/api/users”地址的请求将被重定向到”http://10.10.10.10:3000/api/users“,如果不希望”api”在传递中被传递过去,可以使用rewrite的方式实现:
1 2 3 4 5 6 |
proxy: { "/api": { target: "http://localhost:3000", pathRewrite: {"^/api" : ""} } } |
11、publicPath
用于设置编译后文件的路径,假设服务器的运行地址是 http://localhost:8080,输出文件名设置为bundle.js,那么默认情况下publicPath是”/”,因此文件地址为http://localhost:8080/bundle.js 如果想要设置为别的路径可以这样:
publicPath: "/assets/"
设置后文件地址为:http://localhost:8080/assets/bundle.js
注意:
确保publicPath的书写规则:前后都有一个斜杠!
webpack配置动态加载
有时候,不同环境,需要加载不同的配置,以在开发环境中,webpack-dev-server开启热加载,需要加载两个插件为例:
打开webpack.config.js,配置一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
const config = {//把webpack的配置作为一个对象,使用这个对象,就可以动态修改参数 mode: "development", entry:{ app:path.join(__dirname,'./src/main.js') }, output:{ filename:'[name].[hash:8].js', path:path.join(__dirname,'./dist') }, module: { rules: [ { test:/\.vue$/, loader:"vue-loader" }, { test:/\.css$/, use: ["vue-style-loader","css-loader"] }, { test:/\.(jpg|jpeg|png|gif|svg)$/, use:[ { loader: "url-loader", options:{ limit:1024, filename:"[name].[chunkhash:8].[ext]" } } ] } ] }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ template: 'index.html', filename: 'index.html' }), new VueLoaderPlugin(), ] } if (isDev){ config.devServer = { port: '8000', host: '0.0.0.0', overlay:{ errors:true }, hot:true,//代码热更新,改动代码,不刷新整个页面,只刷新改动代码的那个组件页面 } config.plugins.push(//动态调用插件 //热处理的插件 new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin() ) } |
webpack调试工具:devtool
在网页调试代码,因为我们写的代码被编译过,调试的时候看不懂。需要借助devtool,做一下源码的映射,调试的时候显示源码。
在webpack.config.js配置:
1 2 3 4 5 6 7 8 |
if (isDev){ //在网页调试代码,因为我们写的代码被编译过,看不懂。使用source-map进行源码的映射 config.devtool = '#cheap-module-eval-source-map'//这里有多种源码映射模式 config.devServer = { port: '8000', host: '0.0.0.0' } } |
render与template使用的vue文件有所不同
刚开始,入口文件vue的渲染是使用render()方法渲染的:
1 2 3 4 5 6 7 |
import Vue from 'vue' import App from './App.vue' const app = new Vue({ el:"#root", render:(h) => h(App) }); |
其实,vue的渲染除了render()方法外还可以使用vue的template属性来做:
1 2 3 4 5 6 7 8 |
import Vue from 'vue' const app = new Vue({ el:"#root", template:` <div>我是template 我在这</div> ` }); |
这时,运行的时候发现报错了:
关键词是:“ runtime-only”。
原来vue js源码文件,有三种,分别是vue.js、vue.common.js和compiler.js。默认情况下,如果使用
import vue from 'vue'
导入vue, vue 默认导出的是 vue.common.js,也就是说默认下我们使用的vue源码文件是vue.common.js。为什么 vue 默认导出的是 vue.common.js,它和 vue.js 的区别在哪里,又有什么关系?
Vue 最早会打包生成三个文件,一个是 runtime only 的文件 vue.common.js,一个是 compiler only 的文件 compiler.js,一个是 runtime + compiler 的文件 vue.js。
也就是说,vue.js = vue.common.js + compiler.js,而如果要使用 template 这个属性的话就一定要用 compiler.js,那么,引入 vue.js 是最恰当的。
这也就说明了为什么默认情况下使用vue的template属性会报“runtime-only”的错误。
解决方法
在webpack给vue别名重新指定vue文件,声明import Vue from 'vue'
中的vue使用的是哪个vue文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const config = { mode: "development", entry:{ app:path.join(__dirname,'./src/main.js') }, output:{ filename:'[name].[hash:8].js', path:path.join(__dirname,'./dist') }, module: { 。。。省略。。。 }, plugins: [ 。。。省略。。。 ], resolve: { alias:{//重新指定vue,是使用哪个vue文件 "vue":'vue/dist/vue.js' } } } module.exports = config; |
配置vue的jsx写法以及postcss
postcss
PostCSS是什么?
它可以被理解为一个平台,可以让一些插件在上面跑
它提供了一个解析器,可以将CSS解析成抽象语法树
通过PostCSS这个平台,我们能够开发一些插件,来处理CSS。热门插件如autoprefixer。
执行命令:npm install postcss-loader autoprefixer
安装这些模块。
在根目录下创建postcess.config.js用于配置postcss:
1 2 3 4 5 6 7 8 |
const autoprefixer = require('autoprefixer') //postcss帮我们后处理css,主要优化css,这个优化由autoprefixer完成 //autoprefixer的工作,比如,加浏览器前缀,例:-webkit-transform,-webkit就是一个浏览器前缀 module.exports={ plugins:[ autoprefixer() ] } |
然后,来到webpack.config.js来配置一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
const path = require('path'); const HTMLPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const isDev = process.env.NODE_ENV == 'development'; const config = { target: "web", entry:path.join(__dirname,'src/index'), output: { filename: "bundle.js", path: path.join(__dirname,'dist') }, module: { rules: [ { test:/\.vue$/, loader:'vue-loader' }, {//.jsx的文件,都使用babel-loader处理 test:/\.jsx$/, loader:'babel-loader' }, { test:/\.css$/, use:[ 'style-loader', 'css-loader' ] }, { test:/\.styl$/, use:[ 'style-loader', 'css-loader', { loader: "postcss-loader",//使用postcss-loader处理 options:{//前面stylus-loader把源码生成了source-map,把sourceMap改为true,postcss-loader可以使用前面生成的source-map,无需再生成一遍,提高效率 sourceMap:true } }, 'stylus-loader' ] }, { test:/\.(jpg|jpeg|png|gif|svg)$/, use:[ { loader: "url-loader", options:{ limit:1024, name: '[name]-aaa.[ext]' } } ] } ] }, plugins: [ new webpack.DefinePlugin({ "process.env":{ NODE_ENV:isDev?'"development"':'production' } }), new HTMLPlugin() ] } if (isDev){ config.devtool = '#cheap-module-eval-source-map' config.devServer = { port: '8000', host: '0.0.0.0', overlay:{ errors:true }, open:true, hot:true, } config.plugins.push( new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin() ) } module.exports = config; |
jsx
因为jsx的语法需要es6支持,所以需要安装babel:
执行命令:npm install babel-core babel-loader
如果安装的babel-loader版本在8.0以上的,要求babel-core的版本在7.0以上,不过babel-core的7.0以上的版本需要通过这样的命令安装:npm install @babel/core
在根目录下创建.babelrc
用于配置babel:
1 2 3 4 5 6 7 8 |
{ "presets": [ "env" ], "plugins": [ "transform-vue-jsx"//转换vue里的js代码 ] } |
执行命令:npm install babel-preset-env babel-plugin-transform-vue-jsx babel-helper-vue-jsx-merge-props
babel preset将基于你的实际浏览器及运行环境,自动的确定babel插件及polyfills,转译ES2015及此版本以上的语言,在没有配置项的情况下,你也可以通过配置polyfills和transforms来支持你所需要支持的浏览器,仅配置需要支持的语法来使你的打包文件更轻量级。
来安装所使用的到的模块。安装完后可能会提示还需要安装其它依赖,按提示,自己手动安装就好了。
jsx的demo
jsx其实和vue文件的结构相似,vue的html代码写在template里,而jsx则写在了render方法中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import './../css/footer.sass'//样式问题,可以通过引入样式文件解决 export default {//这和vue的写法非常相似 data(){ return { author:'benz' } }, render(){//jsx的html代码写在了render方法里,但是JavaScript语法却和vue不一样 return ( <div id="footer"> <span class="footer_font">这是--{this.author}--出品</span> </div> ) } } |
定义了一个jsx,可以看做是一个vue组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<template> <div id="app"> <Footer/>//使用 </div> </template> <script> import Footer from './jsx/footer.jsx'//引入jsx文件 export default { name: "App", components:{//jsx作为vue的组件 Footer } } </script> <style scoped> </style> |
sass文件如下:
1 2 3 |
.footer_font $footer_font_color: red color: $footer_font_color |
css单独分离打包
执行命令:
npm install extract-text-webpack-plugin
安装好插件。
来到webpack.config.js进行配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
const path = require('path'); const HTMLPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const ExtractPlugin = require('extract-text-webpack-plugin');//引入插件 const isDev = process.env.NODE_ENV == 'development'; const config = { target: "web", entry:path.join(__dirname,'src/index'), output: { filename: "[name].[hash:8].js",//这里使用了哈希值自动命名 path: path.join(__dirname,'dist') }, module: { rules: [ { test:/\.vue$/, loader:'vue-loader' }, { test:/\.jsx$/, loader:'babel-loader' }, { test:/\.(jpg|jpeg|png|gif|svg)$/, use:[ { loader: "url-loader", options:{ limit:1024, name: '[name]-aaa.[ext]' } } ] } ] }, plugins: [ new webpack.DefinePlugin({ "process.env":{ NODE_ENV:isDev?'"development"':'production' } }), new HTMLPlugin() ] } if (isDev){ //使用css分离打包,要区分环境,只能 根据环境动态添加规则 config.module.rules.push( { test:/\.styl$/, use:[ 'style-loader', 'css-loader', { loader: "postcss-loader", options:{ sourceMap:true } }, 'stylus-loader' ] } ), config.devtool = '#cheap-module-eval-source-map', config.devServer = { port: '8000', host: '0.0.0.0', overlay:{ errors:true }, open:true, hot:true, }, config.plugins.push( new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin() ) }else { config.output.filename = '[name].[chunkHash:8].js' //这里我们想在生产环境下使用CSS分离打包 config.module.rules.push( { test:/\.styl$/, use:ExtractPlugin(//使用extract-text-webpack-plugin使css代码分离 { fallback: "style-loader",//把css-loader处理后的内容在外面包了一层js代码,这js代码是把css代码写到html里面去 use:[ 'css-loader', { loader: "postcss-loader", options:{ sourceMap:true } }, 'stylus-loader' ] } ) } ) config.plugins.push( new ExtractPlugin('styles.[contentHash:8].css')//配置css单独分离后的文件名称,[contentHash:8]是内置的,根据文本内容生成哈希值 ) } module.exports = config; |
在命令行,执行命令:npm run build
,就可以在dist文件夹下看见生成的文件。其中有一个css文件,那是分离css单独打包的,但是里面不包含每个vue主键的css代码。
webpack区分打包类库代码及hash优化
为什么要把vue文件和其他类库分开打包,这是应为这些文件是比较稳定的,一般改动都是一些业务文件,只需更新业务文件就可以了。而不用修改的文件浏览器也会一直缓存起来,加快加载速度。
打开webpack.config.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
const path = require('path'); const HTMLPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const ExtractPlugin = require('extract-text-webpack-plugin'); const isDev = process.env.NODE_ENV == 'development'; const config = { target: "web", entry:path.join(__dirname,'src/index'), output: { filename: "[name].[hash:8].js", path: path.join(__dirname,'dist') }, module: { rules: [ { test:/\.vue$/, loader:'vue-loader' }, { test:/\.jsx$/, loader:'babel-loader' }, { test:/\.(jpg|jpeg|png|gif|svg)$/, use:[ { loader: "url-loader", options:{ limit:1024, name: '[name]-aaa.[ext]' } } ] } ] }, plugins: [ new webpack.DefinePlugin({ "process.env":{ NODE_ENV:isDev?'"development"':'production' } }), new HTMLPlugin() ] } if (isDev){ config.module.rules.push( { test:/\.styl$/, use:[ 'style-loader', 'css-loader', { loader: "postcss-loader", options:{ sourceMap:true } }, 'stylus-loader' ] } ), config.devtool = '#cheap-module-eval-source-map', config.devServer = { port: '8000', host: '0.0.0.0', overlay:{ errors:true }, open:true, hot:true, }, config.plugins.push( new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin() ) }else { //vue与类库分开打包,首先定义一个入口 config.entry = { app:path.join(__dirname,'src/index'), vendor:['vue']//需要分开打包的框架,还可以填其它的框架,比如,vue-router等等 } config.output.filename = '[name].[chunkHash:8].js' config.module.rules.push( { test:/\.styl$/, use:ExtractPlugin( { fallback: "style-loader", use:[ 'css-loader', { loader: "postcss-loader", options:{ sourceMap:true } }, 'stylus-loader' ] } ) } ) config.plugins.push( new ExtractPlugin('styles.[contentHash:8].css') new webpack.optimize.CommonsChunkPlugin({//配置单独打包的选项 name: 'vendor'//这个名字,要和entry字段一致 }) ) } module.exports = config; |
hash与chunkhash区别:
hash一次打包所生成的hash值都一样的,chunkhash则是不同的。
从webpack4开始官方移除了commonchunk插件,改用了optimization属性进行更加灵活的配置,这也应该是从V3升级到V4的代码修改过程中最为复杂的一部分,下面的代码即是optimize.splitChunks 中的一些配置参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
//optimization与entry/plugins同级 optimization: { runtimeChunk: { name: "manifest" }, minimize: true, splitChunks: { name: false, chunks: "async", minSize: 30000, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, cacheGroups: { vendor: { name: "vendor", chunks: "initial", priority:-10, reuseExistingChunk: false, test: /node_modules\/(.*)\.js/ }, styles: { name: "styles", chunks: "all", reuseExistingChunk: true, test: /\.(scss|sass|css)$/, minChunks: 1 } } } } |
稍微解释一下含义
- cacheGroups:可以自己设置一组一组的cache group来配对应的共享模块
- commons:里面的name就是生成的共享模块bundle的名字
- chunks:有三个可选值,”initial”, “async” 和 “all”. 分别对应优化时只选择初始的chunks,所需要的chunks 还是所有chunks 。
- minChunks :是split前,有共享模块的chunks的最小数目 ,默认值是1, 但我看示例里的代码在default里把它重写成2了,从常理上讲,minChunks = 2 应该是一个比较合理的选择吧。
项目webpack整理
在项目根目录新建一个名为build文件夹,统一存放webpack等配置文件
webpack配置文件抽离
webpack配置抽离后,需要多个零散的webpack配置文件,共同组成一份新的,完整的,符合环境的webpack配置文件。这个需要webpack-merge的帮助。执行命令:npm install webpack-merge
安装好webpack-merge。
在build文件夹新建名为webpack.config.base.js的文件,里面是公共的webpack配置,无论是什么环境:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
//基础的公共的配置 const path = require('path'); const isDev = process.env.NODE_ENV == 'development'; const config = { target: "web", entry:path.join(__dirname,'../src/index'),//这个是会被不同环境下的参数覆盖,这里只作为一个默认值//目录结构发生了变化,修改一下 output: { filename: "[name].[hash:8].js", path: path.join(__dirname,'dist') }, module: { rules: [ { test:/\.vue$/, loader:'vue-loader' }, { test:/\.jsx$/, loader:'babel-loader' }, {//js文件的处理 test:/\.js$/, loader:'babel-loader', exclude:/node_modules/ //有些js文件是不需要处理的 }, { test:/\.(jpg|jpeg|png|gif|svg)$/, use:[ { loader: "url-loader", options:{ limit:1024, name: 'resource/[path][name].[hash:8].[ext]'//文件改为hash命名,并且统一放到一个文件夹上 } } ] } ] } } module.exports = config; |
在build文件夹新建名为webpack.config.client.js的文件,里面是客户端的webpack配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
//开发环境的webpack配置 const path = require('path'); const HTMLPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const ExtractPlugin = require('extract-text-webpack-plugin'); const merge = require('webpack-merge');//引入webpack-merge,合并配置项 const baseConfig = require('webpack.config.base');//引入webpack基本配置 const isDev = process.env.NODE_ENV == 'development'; //webpack插件默认的配置 const defaultPlugins = [ new webpack.DefinePlugin({ "process.env":{ NODE_ENV: isDev?'"development"':'"production"' } }), new HTMLPlugin() ]; //webpack-dev-server的配置 let devServer = { port: '8000', host: '0.0.0.0', overlay:{ errors:true }, open:true, hot:true, }; let config; if (isDev){ //merge会生成新的配置,不会修改原配置 config = merge(baseConfig,{ devtool: '#cheap-module-eval-source-map', module: { rules:[ { test:/\.styl$/, use:[ 'style-loader', 'css-loader', { loader: "postcss-loader", options:{ sourceMap:true } }, 'stylus-loader' ] } ] }, devServer, plugins:defaultPlugins.concat([//默认插件配置合并在一起 new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin() ]) }) }else { config.merge(baseConfig,{ entry:{ app:path.join(__dirname,'../src/index'),//目录结构发生了变化,修改一下 vendor:['vue'] }, output:{ filename: '[name].[chunkHash:8].js' }, module:{ rules: [ { test:/\.styl$/, use:ExtractPlugin( { fallback: "style-loader", use:[ 'css-loader', { loader: "postcss-loader", options:{ sourceMap:true } }, 'stylus-loader' ] } ) } ] }, plugins:defaultPlugins.concat([//默认插件配置合并在一起 new ExtractPlugin('styles.[contentHash:8].css'), new webpack.optimize.CommonsChunkPlugin({ name: 'vendor' }), new webpack.optimize.CommonsChunkPlugin({ name: 'runtime' }) ]) }) } module.exports = config; |
因为路径改变,需要修改一下package.json里的脚本启动命令:
1 2 3 4 |
"scripts": { "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.client.js", "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.client.js" }, |
最后运行npm run dev
或npm run build
,没问题就好了。
vue-loader配置
在build文件夹新建文件vue-loader.config.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//vue-loader 配置项 const docsLoader = require.resolve('./doc-loader') module.exports = (isDev)=>{ return { preserveWhitepace:true, //去除vue的空格地方 extractCSS: true, //打包是否可以把vue里面的样式单独抽离出来打包 cssModule:{}, postcss:[],//一般会单独有一份配置文件,在这里写是全局的 hotReload:true, //vue热重载 loaders:{//自定义vue模块,template、style、script是vue的模块 docs:docsLoader, js:"coffe-loader"//script将会由coffe-loader处理 }, preLoader:{},//在loader之前先用指定的loader处理一遍 postLoader:{},//在loader之后用指定的loader处理一遍 } } |
在公共配置webpack.config.base.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
//基础的公共的配置 const path = require('path'); const createVueLoaderOptions = require('./vue-loader.config');//引入vue-loader配置文件 const isDev = process.env.NODE_ENV == 'development'; const config = { target: "web", entry:path.join(__dirname,'../src/index'), output: { filename: "[name].[hash:8].js", path: path.join(__dirname,'dist') }, module: { rules: [ { test:/\.vue$/, loader:'vue-loader', options:createVueLoaderOptions(isDev)//使用vue-loader配置 }, { test:/\.jsx$/, loader:'babel-loader' }, {//js文件的处理 test:/\.js$/, loader:'babel-loader', exclude:/node_modules/ }, { test:/\.(jpg|jpeg|png|gif|svg)$/, use:[ { loader: "url-loader", options:{ limit:1024, name: 'resource/[path][name].[hash:8].[ext]' } } ] } ] } } module.exports = config; |
打包前清空打包输出的目录
这个需要借助rimraf,执行命令:
npm install rimraf
安装rimraf,并在package.json的脚本标签配置一下:
1 2 3 4 5 6 |
"scripts": { "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.config.client.js", "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.client.js", "clean": "rimraf dist",//dist是要删除的文件夹 "build": "npm run clean && npm run build:client"//先清空输出文件夹,再打包 }, |
vue-loader的cssModule配置
在vue-loader.config.js文件中:
1 2 3 4 5 6 7 8 9 10 11 |
//vue-loader 配置项 module.exports = (isDev)=>{ return { preserveWhitepace:true, extractCSS: true, cssModule:{ localIdentName:'[path]-[name]-[hash:base64:5]',//css单独打包的名字 camelCase:true,//把class名字中有‘-’转为驼峰命名,以适应vue的使用,如:‘class="login-once"’会转为‘class="loginOnce"’ } } } |
在每个vue组件的style加上module,表示这个style是使用CSSModule处理的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<template> <div id="app">{{text}}</div> <Footer :class="$style.footer"></Footer><!--$style是CSSModule把vue的css变成了obj--> </template> <script> import Footer from './view/footer.jsx'; export default { name: "App", comments:{ Footer }, data(){ return { text:'我在这' } } } </script> //使用了CSSModule,要在style这里加一个module <style scoped module> #app { color: red; } .footer { color: blue; } </style> |
在webpack配置文件,配置使用CSSModule
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
module: { rules:[ { test:/\.styl$/, use:[ 'style-loader', {//把css-loader改为CSSModule模式处理 loader: "css-loader", options:{ module:true, localIdentName:'[path]-[name]-[hash:base64:5]' } }, { loader: "postcss-loader", options:{ sourceMap:true } }, 'stylus-loader' ] } ] } |
安装eslint规范代码写作,editorconfig统一编辑器规范,precommit代码不规范不许git提交
eslint
执行命令:
npm install eslint eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node
安装eslint以及其依赖,这里的规范使用eslint提供的标准规则,也可以使用其他的规范,可以更改。
eslint检测vue下的js代码,需要借助eslint-plugin-html,才能检测到vue的js代码。执行命令:npm install eslint-plugin-html
,安装插件。
安装完成后,在项目根目录创建名为:.eslintrc
,作为eslint的配置文件:
1 2 3 4 5 6 |
{ "extends":"standard",//使用标准规则 "plugins": [//需要用到的插件,这里用到eslint-plugin-html,所以写了个"html" "html" ] } |
在package.json使用eslint:
1 2 3 4 5 6 7 |
"scripts": { "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.config.client.js", "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.client.js", "clean": "rimraf dist", "lint": "eslint --ext .js --ext .jsx --ext .vue client/",//--ext 要检测的文件 检测的路径 "build": "npm run clean && npm run build:client" }, |
执行命令:npm run lint
,就会检测代码的编写规范,你会发现有很多规范上的错误,eslint提供自动修复功能:
1 2 3 4 5 6 7 8 |
"scripts": { "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.config.client.js", "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.client.js", "clean": "rimraf dist", "lint": "eslint --ext .js --ext .jsx --ext .vue client/", "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue client/",//加多一个参数 --fix eslint就可以自动修复 "build": "npm run clean && npm run build:client" }, |
执行命令:npm run lint-fix
,代码的错误的编写会被自动修复。
使用eslint实时检测代码规范
这个需要安装一些插件依赖,执行命令:npm install eslint-loader babel-eslint
,安装完后,先打开.eslintrc,配置一下:
1 2 3 4 5 6 7 |
{ "extends":"standard", "plugins": [ "html" ], "parser": "babel-eslint"//webpack的代码都是babel处理过的,babel的语法可能对eslint不是特别的支持 } |
在webpack.config.base.js中配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
module: { rules: [ { test:/\.(vue|js|jsx)$/,//eslint要校验的文件 loader:'eslint-loader',//使用eslint-loader来处理 exclude: /node_modules/,//这个文件夹无需检测 enforce: "pre"//使用是预处理模式,因为vue的处理还由vue-loader来处理,eslint只是在这之前检测规范就好 }, { test:/\.vue$/, loader:'vue-loader', options:createVueLoaderOptions(isDev) }, { test:/\.jsx$/, loader:'babel-loader' }, { test:/\.js$/, loader:'babel-loader', exclude:/node_modules/ }, { test:/\.(jpg|jpeg|png|gif|svg)$/, use:[ { loader: "url-loader", options:{ limit:1024, name: 'resource/[path][name].[hash:8].[ext]' } } ] } ] } |
eslint到此告一段落。
editorconfig
editorconfig是用来规范编辑器的配置的。
在项目根目录下创建文件:.editorconfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
root = true # 对所有文件生效 [*] # utf-8编码 charset = utf-8 # 空格形式缩进4空格 indent_style = space indent_size = 4 # Linux换行符 end_of_line = lf # 结尾插入新行 insert_final_newline = false # 去除结尾空格 trim_trailing_whitespace = true # 对后缀名为 md 的文件生效 [*.md] trim_trailing_whitespace = false # 对后缀名为 html, js, blade.php 的文件生效 [*.{html,js,blade.php}] indent_style = space indent_size = 2 |
注意:这个需要编辑器的支持,如果不支持,可能需要安装对应的编辑器的插件。
precommit
如果,代码不符合规范,就不允许提交到git上面去,这要借助husky,执行命令:npm install husky
安装,再去package.json配置脚本:
1 2 3 4 5 6 7 8 9 |
"scripts": { "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.config.client.js", "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.client.js", "clean": "rimraf dist", "lint": "eslint --ext .js --ext .jsx --ext .vue client/", "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue client/", "precommit": "npm run lint-fix",//husky的使用 "build": "npm run clean && npm run build:client" }, |
最后,执行命令:git commit -m "项目名"
,如果代码有错,是不能提交的。
注意:项目必须有git目录,也就是项目已经git初始化过,否则husky会报错。
最后
这是基于webpack3写的,现在默认都是webpack4,还有,可能写的不好,这里有些别人写的webpack4的文章:
webpack4+vue打包简单入门
【webpack】webpack-dev-server生猛上手——让我们来搭一个webpack的微服务器吧!
webpack从零开始第1课:安装webpack和webpack-dev-server
深入浅出的webpack构建工具—webpack基本配置(一)
以下是这次webpack3依赖的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
{ "name": "muke_vuewebpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "cross-env NODE_ENV=production webpack --config webpack.config.js", "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js" }, "author": "", "license": "ISC", "devDependencies": { "css-loader": "^0.28.7", "file-loader": "^1.1.6", "style-loader": "^0.19.1", "stylus": "^0.54.5", "stylus-loader": "^3.0.1", "url-loader": "^0.6.2", "vue-loader": "^13.6.0", "vue-template-compiler": "^2.5.13", "webpack": "^3.10.0", "autoprefixer": "^7.2.3", "babel-core": "^6.26.0", "babel-helper-vue-jsx-merge-props": "^2.0.0", "babel-loader": "^7.1.2", "babel-plugin-syntax-jsx": "^6.8.0", "babel-plugin-transform-vue-jsx": "^3.5.0", "babel-preset-env": "^1.6.1", "cross-env": "^5.1.3", "extract-text-webpack-plugin": "^3.0.2", "html-webpack-plugin": "^2.30.1", "postcss-loader": "^2.0.9", "webpack-dev-server": "^2.9.7" }, "dependencies": { "vue": "^2.5.13" } } |
css热更新
要实现css热更新,使用的处理器是vue-style-loader
,而不是style-loader。