10.使用webpack

1.高效的开发离不开基础工程的搭建。
2.近几年来,前端的工作早已不再是切图那么简单,项目比较大时,可能会多人协同开发。模块化、组件化、CSS预编译等概念也成了经常讨论的话题。
前端自动化(半自动化)工程主要解决以下问题:

  • JavaScript、CSS代码的合并和压缩
  • CSS 预处理:Less、Sass、Stylus的编译
  • 生成雪碧图(CSS Sprite)
  • ES6 转 ES5
  • 模块化
    3.webpack打包后的代码,已经不只是你写的代码,其中夹杂了很多webpack自身的模块处理代码。因此,学习webpack最难的是理解“编译”这个概念,否则会一直存在一个疑问:为什么要这样做?
    4.在业务中写的各种格式的文件,比如typescript、less、jpg,还有本章后面要介绍的.vue格式的文件。这些格式的文件通过特定的加载器(Loader)编译后,最终统一生成为.js、.css、.png等静态资源文件。在webpack的世界里,一张图片、一个css甚至一个字体,都称为模块(Module),彼此存在依赖关系,webpack就是来处理模块间的依赖关系的,并把它们进行打包。
    举一个简单的例子,平时加载CSS大多通过<link>标签引入CSS文件,而在webpack里,直接在一个.js文件中导入,比如:
    import 'src/styles/index.css';
    import 是 ES2015的语法,这里也可以写成 require('src/styles/index.css')。在打包时,index.css会被打包进一个js文件里,通过动态创建<style>的形式来加载css样式,当然也可以进一步配置,在打包编译时把所有的css都提取出来,生成一个css的文件。
    5.webpack的主要使用场景是单页面富应用(SPA)。SPA通常是由一个html文件和一堆按需加载的js组成,它的html结构可能会非常简单,比如:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>webpack app</title>
    <link rel="stylesheet" href="dist/main.css">
</head>
<body>
 <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
 <div id="app"></div>

<script type="text/javascript" src="dist/main.js"></script>

</body>
</html>

只有一个<div>节点,所有的代码都集成在了main.js文件中,理论上它可以实现像知乎、淘宝这样大型的项目。
6.在开始讲解webpack的用法前,先介绍两个ES6 中的语法export和import,后面会大量使用。export和import是用来导出和导入模块的。一个模块就是一个js文件,它拥有独立的作用域,里面定义的变量外部是无法获取的。比如将一个配置文件作为模块导出,示例代码如下:

// config.js
var Config = {
  version:'1.0.0'
};
export { Config };
// 或者 config.js
export var Config = {
  version: '1.0.0'
};
//其他类型(比如函数、数组、常量等)也可导出,比如导出一个函数:
// add.js
export function add(a,b){
  return a + b;
};
//模块导出后,在需要使用模块的文件使用import再导入,就可以在这个文件内使用这些模块了。示例代码:
//main.js
import {Config } from './config.js';
import { add } from './add.js';
console.log(Config); //{version : '1.0.0'}
console.log(add(1,1)); //2

以上几个示例中,导入的模块名称都是在export的文件中设置的,也就是说用户必须预先知道这个名称叫什么,比如Config、add。而有的时候,用户不想去了解名称是什么,只是把模块的功能拿来使用,或者想自定义名称,这是可以使用export default 来输出默认的模块。示例代码:

//config.js
export default {
  version:'1.0.0'
};
//add.js
export default function(a,b){
  return a + b ;
}
//main.js
import conf from './config.js';
import Add from './add.js';
console.log(conf); // {version:'1.0.0'}
console.log(Add(1,1)); // 2
//如果使用npm安装了一些库,在webpack中可以直接导入,示例:
import Vue from 'vue';
import $ from 'jquery';

上例分别导入了Vue和jQuery的库,并且命名为Vue和$,在这个文件中就可以使用这两个模块。export 和 import 还有其他的用法,这里不做太详细的介绍。
7.安装webpack与 webpack-dev-server。确保意境安装了最新的Node.js和NPM,并已经了解NPM的基本用法。

首先,创建一个目录,比如demo,使用NPM初始化配置:
npm init
执行后,会有一些列选项,可以按回车键快速确认,完成后会在demo目录生产一个package.json的文件。
之后在本地局部安装webpack:
npm install webpack --save-dev
--save-dev 会作为开发依赖来安装webpack。安装成功后,在package.json中会多一项配置:
"devDependencies":{
"webpack" : "^2.3.2"
}
接着需要安装webpack-dev-server,它可以在开发环境中提供很多服务,比如启动一个服务器、热更新、接口代理等,配置起来也很简单。同样,在本地局部安装:
npm install webpack-dev-server --save-dev
安装完成后,最终的package.json文件内容为:

{
  "name" : "demo",
  "version" : "1.0.0",
  "description" : "",
  "main" : "index.js",
  "scripts" : {
    "test" : "echo \"Error : no test specified \" && exit 1"
  },
  "author" : "",
  "license" : "ISC",
  "devDependencies" : {
    “webpack” : "^2.3.2",
    "webpack-dev-server" : "^2.4.2"
  }
}

如果devDependencies中包含 webpack 和 webpack-dev-server,那么已经安装成功。
8.webpack核心概念。webpack就是一个.js配置文件,你的架构好或差都体现在这个配置里,随着需求的不断出现,工程配置也是逐渐完善的。

首先,在目录DEMO 下创建一个js文件:webpack.config.js,并初始化它的内容:
var config = {
};
module.exports = config;
这里的 module.exports = config; 相当于 export default config;
由于目前还没有安装支持ES6的编译插件,因此不能直接使用ES6的语法,否则会报错。
然后在package.json的scripts里增加一个快速启动webpack-dev-server服务的脚本:
{
//...
"scripts" : {
"test" : "echo "Error: no test specified " && exit 1",
"dev" : "webpack-dev-server --config webpack.config.js"
},
//...
}
当运行 npm run dev 命令时,就会执行
webpack-dev-server --open --config webpack.config.js 命令。
其中,--config 是指向 webpack-dev-server 读取的配置文件路径,这里直接读取我们在上一步创建的 webpack.config.js 文件。
--open 会在执行命令时自动在浏览器打开页面,默认地址是 127.0.0.1:8080,不过IP和端口号都是可以配置的,如:

{
  "scripts" : {
    "dev" : "webpack-dev-server  --host  172.172.172.1 --port 8888 --open --config webpack.config.js"
  }
}

这样访问地址就改为了 172.172.172.1:8888。一般在局域网下,需要让其他同事访问时可以这样配置。

webpack配置中最重要也是必选的两项是入口(Entry)和 出口(Output)。入口的作用是告诉webpack从哪里开始寻找依赖,并且编译,出口则用来配置编译后的文件存储位置和文件名。
在demo目录下新建一个空的main.js作为入口的文件,然后在webpack.config.js中进行入口和输出的配置:

var path = require('path');
var config = {
  entry:{
    main : './main'  
  },
  output:{
    path : path.join(_dirname, './dist'),
    publicPath : '/dist/',
    filename : 'main.js'
  }
};
module.exports = config;

entry中的main就是我们配置的单入口。webpack会从main.js文件开始工作。output中path选项用来存放打包后的输出目录,是必填项。publicPath指定资源文件引用的目录,如果你的资源存放在CDN上,这里可以填CDN的网址。filename用于指定输出文件的名称。因此,这里配置的output意为打包后的文件会存储为 demo/dist/main.js 文件,只要在html 中引入它就可以了。在demo目录下,新建一个index.html 作为我们 SPA 的入口:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>webpack APP</title>
</head>
<body>
    <div id="app">
        Hello World.
    </div>
    <script type="text/javascript" src="/dist/main.js"></script>
</body>
</html>

在终端执行如下命令,就会自动在浏览器中打开页面:

npm run dev

打开dome/main.js文件,添加一行JavaScript代码来修改页面内容:

document.getElementById('app').innerHTML = 'Hello webpack.';

保存文件,回到刚才打开的页面,发现页面内容已经变为了“Hello webpack”。注意,此时并没有刷新浏览器,就已经自动更新了,这就是webpack-dev-server 的热更新功能,它通过建立一个WebSocket 连接来实时响应代码的修改。
9.学习webpack最难的是理解它“编译”的概念。我们通过chrom浏览器开发者工具的network视图查看webpack编译出的 /dist/main.js 文件。可以看到有400多行。这里面很多都是webpack-dev-server的功能,只在开发时有效,生产环境下编译就不会这么臃肿了。执行下面的命令打包:

webpack --progress --hide-modules

这是会生成一个 demo/dist/main.js 文件,只有很少的行数。
10.上面通过配置入口(entry)和出口(Output)已经可以启动webpack项目了,不过这并不是webpack的特点,如果它只有这些功能,根本就不用这么麻烦。webpack.config.js可以进一步配置,来实现更强大的功能。
在webpack的世界里,每个文件都是一个模块,比如.css、.js、.html、.jpg、.less等。对于不同的模块,需要用不同的加载器(Loaders)来处理,而加载器就是webpack最重要的功能。通过安装不同的加载器可以对各种后缀名的文件进行处理,比如要写一些CSS样式,就要用到style-loader 和 css-loader。下面就通过NPM 来安装它们:

npm install css-loader --save-dev
npm install style-loader --save-dev

安装完成后,在webpack.config.js 文件里配置Loader,增加对.css 文件的处理:

var config = {
  //...
  module:{
    rules:[
      {
        test: /\.css$/,
        use:[
          'style-loader',
          'css-loader'
        ]
      }
    ]
  }
};
module.exports = config;

在module对象的rule属性中可以指定一系列的loaders,每一个loader都必须包含test和use两个选项。这段配置的意思是说,当webpack编译过程中遇到 require()或 import 语句导入一个后缀名为 .css 的文件时,先将它通过 css-loader转换,再通过style-loader 转换,然后继续打包。 use选项的值可以是数组或字符串,如果是数组,它的编译数序就是从后往前。
在demo目录下新建一个style.css的文件,并在main.js中导入:

/* style.css */
#app{
    font-size:24px;
    color:#f50;
}
// main.js
import './style.css';
document.getElementById('app').innerHTML = 'Hello webpack.';

重新执行npm run dev 命令,可以看到页面中的文字已经变成红色,并且字号也变大了。
11.可以看到,CSS是通过JavaScript动态创建 <style> 标签来写入的,这意味着样式代码都已经编译在了main.js文件里,但在实际业务中,可能并不希望这样做,因为项目大了样式会很多,都放在JS里太占体积,还不能做缓存。这是要用到webpack的一个重要概念——插件(Plugins)
webpack的插件功能很强大,而且可以定制。这里我们使用一个 extract-text-webpack-plugin的插件来把散落在各地的css提取出来,并生成一个main.css 的文件,最终在index.html里通过<link>的形式加载它。
通过NPM安装 extract-text-webpack-plugin 插件:
npm install extract-text-webpack-plugin --save-dev
然后在配置文件中导入插件,并改写loader的配置:

//导入插件
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var config = {
  //...
  module:{
    rules:[
      {
        test: /\.css$/,
        use:ExtractTextPlugin.extract({
           use:'css-loader',
           fallback:'style-loader' 
        })
      }
    ]
  },
  plugins:[
    //重命名提取后的css文件
    new ExtractTextPlugin("main.css")
  ]
};
module.exports = config ;

// index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>webpack APP</title>
    <link rel="stylesheet" type="text/css" href="/dist/main.css">
</head>
.....

插件还可以进行丰富的配置。重启服务后,就可以看到<style>已经没有了,通过<link>引入的main.css 文件已经生效。
webpack 虽然概念比较新,看似复杂,但它只不过是一个js配置文件,只要搞清楚入口(Entity),出口(Output)、加载器(Loaders)和 插件(Plugins)这4个概念,使用起来就不那么困惑了。
12.单文件组件。之前,在字符串模版template选项里拼写字符串DOM非常费劲,尤其是用""换行。Vue.js是一个渐进式的JavaScript框架,在使用webpack 构建Vue 项目时,可以使用一种新的构建模式:.vue 单文件组件。.vue 单文件组件就是一个后缀名为.vue 的文件,在webpack中使用vue-loader 就可以对.vue 格式的文件进行处理。
一个 .vue 文件一般包含3部分,即 <template>、<script> 和 <style>,如下所示:

<template>
    <div>你好:{{ name }}</div>
</template>
<script>
export default {
    props:{
        name:{
            type:String,
            default:''
        }
    }
}
</script>

<style scoped>
    div{
        color:#f60;
    }
</style>

在component.vue 文件中,<template></template>之间的代码就是该组件的模板HTML,<style></style>之间的是CSS样式,示例中的<style>标签使用了scoped属性,表示当前的CSS只在这个组件有效,如果不加,那么div的样式会用到整个项目。<style>还可以结合CSS预编译一起使用,比如使用 Less处理可以写成 <style lang="less">。
13.使用.vue 文件需要先安装 vue-loader、vue-style-loader 等加载器并做配置。因为要使用ES6 语法,还需要安装babel 和 babel-loader等加载器。
使用npm 逐个安装以下依赖:

npm install --save vue
npm install --save-dev vue-loader
npm install --save-dev vue-style-loader
npm install --save-dev vue-template-compiler
npm install --save-dev vue-hot-reload-api
npm install --save-dev babel
npm install --save-dev babel-loader
npm install --save-dev babel-core
npm install --save-dev babel-plugin-transform-runtime
npm install --save-dev babel-preset-es2015
npm install --save-dev babel-runtime

安装完成后,修改配置文件webpack.config.js来支持对 .vue文件及ES6的解析:

var path = require('path');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var config = {
  entry:{
    main : './main'  
  },
  output:{
    path : path.join(__dirname, './dist'),
    publicPath : '/dist/',
    filename : 'main.js'
  },
  module:{
    rules:[
      {
        test:/\.vue$/,
        loader:'vue-loader',
        options:{
          loaders:{
            css:ExtractTextPlugin.extract({
              use:'css-loader',
              fallback:'vue-style-loader'
            })
          }
        }
      },
      {
        test:/\.js$/,
        loader:'babel-loader',
        exclude:/node_modules/
      },
      {
        test: /\.css$/,
          use:ExtractTextPlugin.extract({
            use:'css-loader',
            fallback:'style-loader' 
          })
      }
    ]
  },
  plugins:[
    //重命名提取后的css文件
    new ExtractTextPlugin("main.css")
  ]
};
module.exports = config;

vue-loader 在编译.vue 文件时,会对<template>、<script>、<style> 分别处理,所以在vue-loader选项里多了一项options来进一步对不同语言进行配置。比如在对 css进行处理时,会先通过css-loader解析,然后把处理结果再交给 vue-style-loader 处理。当你的技术栈多样化时,可以给<template>、<script>、<style> 都指定不同的语言,比如<template lang="jade"> 和 <style lang="less"> , 然后配置loaders就可以了。
在demo目录下新建一个名为.babelrc的文件,并写入babel的配置,webpack 会依赖此配置文件来用babel 编译ES6 代码:

{
  "presets" : ["es2015"],
  "plugins" : ["transform-runtime"],
  "comments" : false
}

配置好这些后,就可以使用.vue 文件了。
每个.vue 文件就代表一个组件,组件之间可以相互依赖。
在工程目录下新建一个app.vue的文件并写入以下内容:

<template>
    <div> Hello {{name}} </div>
</template>
<script>
export default {
    data(){
        return {
            name: 'Vue.js'
        }
    }
}
</script>
<style scoped>
    div{
        color:#f60;
        font-size:24px;
    }
</style>

ES 6 语法提示:data () { } 等同于 data:function() { }
在<template>内写的HTML写法完全同html文件,webpack最终会把它编译为Render 函数的形式。写在<style>里的样式,我们已经用插件 extract-text-webpack-plugin 配置过了,最终会统一提取并打包在main.css 里,因为加了scope 属性,这部分样式只会对当前组件app.vue 有效。
.vue 的组件是没有名称的,在父组件使用时可以对它自定义。写好了组件,就可以在入口main.js 中使用它了。打开main.js 文件,把内容替换为下面的代码:

// 导入 Vue 框架
import Vue from 'vue';
// 导入 app.vue 组件
import App from './app.vue';
// 创建 Vue 根实例
new Vue({
  el: '#app',
  render: h=> h (App)
})

执行命令npm run dev,第一个Vue 工程就跑起来了。
打开chrome调试工具,在elements 面板可以看到,<div id="app"> 已经被组件替换成了:

<div data-v-381730fa> Hello Vue.js </div>

// 对应的main.css 为:
div[data-v-381730fa]{
    color:#f60;
    font-size:24px;
}

之所以多了一串data-v-xxx 的内容,是因为使用了<style scoped>的功能,如果去掉 scoped,就只剩下 <div> Hello Vue.js </div> 了。

解决npm 慢的问题
可用 get命令查看registry
npm congfig get registry
原版结果为
http://registry.npmjs.org
用set命令换成阿里的镜像就可以了
npm config set registry https://registry.npm.taobao.org
在执行命令
npm install

接下来,在demo目录下,再新建两个文件title.vue 和 button.vue

// title.vue:
<template>
    <h1>
        <a :href="'#' + title "> {{ title }} </a>
    </h1>
</template>
<script>
export default {
    props:{
        title:{
            type:String
        }
    }
}
</script>
<style scoped>
    h1 a{
        color:#3399ff;
        font-size:24px;
    }
</style>

// button.vue:
<template>
    <button @click="handleClick" :style="styles">
        <slot></slot>
    </button>
</template>
<script>
export default {
    props:{
        color:{
            type:String,
            default:'#00cc66'
        }
    },
    computed:{
        styles(){
            return {
                background:this.color
            }
        }
    },
    methods:{
        handleClick(e){
            this.$emit('click',e);
        }
    }
}
</script>
<style scoped>
    button{
        border:0;
        outline:none;
        color:#fff;
        padding:4px 8px;
    }
    button:active{
        position:relative;
        top:1px;
        left:1px;
    }
</style>

改写根实例 app.vue 组件,把title.vue 和 button.vue 导入进来:

<template>
    <div>        
        <v-title title="Vue组件化"></v-title>
        <v-button @click="handleClick">点击按钮</v-button>
    </div>
</template>
<script>
//导入组件
import vTitle from './title.vue';
import vButton from './button';

export default {
    components:{
        vTitle,
        vButton
    },
    methods:{
        handleClick(e){
            console.log(e);
        }
    }
}
</script>
<style scoped>
    div{
        color:#f60;
        font-size:24px;
    }
</style>

导入的组件都是局部注册的,而且可以自定义名称,其他用法和组件一致。后面会基于webpack和单文件组件展开。

ES 6 语法提示:
=> 是箭头函数
render: h=> h(App) 等同于:
render : function (h) {
return h (App)
}

components: {
vTitle,
vButton
}
等同于:
components:{
vTitle : vTitle,
vButton : vButton
}
对象字面量缩写,当对象的key 和 value 名称一致时,可以缩写成一个。
14.用于生产环境。我们先对webpack 进一步配置,来支持更多常用的功能。安装url-loader 和 file-loader 来支持图片、字体等文件:

npm install --save-dev url-loader
npm install --save-dev file-loader

//webpack.config.js
var config = {
module:{
    rules:[
      {
        test:/\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
        loader:'url-loader?limit=1024'
      }
}

当遇到.gif、.png、.ttf 等格式文件时,url-loader会把它们一起编译到dist 目录下," ?limit=1024" 是指如果这个文件小余1kb,就以base64的形式加载,不会生成一个文件。
找一张图片,保存为 demo/images/image.png,并在app.vue 中加载它:

<template>
    <div>        
        <v-title title="Vue组件化"></v-title>
        <v-button @click="handleClick">点击按钮</v-button>
        <p>
            <img src="./images/image.png" style="width:200px;">
        </p>
    </div>
</template>

--save 和 --save-dev
在安装包时, 我们可以根据环境决定包的安装效果. 假设我们要安装express
1.执行 npm i express --save则表示我想在生产环境中使用express, 同时, express的版本信息会被写入package.json中的dependencies属性中.
2.而执行npm i express --save-dev 表示我想在开发和测试环境中使用. express的版本信息会被写入package.json中的devDependencies属性中.
3 --save可以简写为-S, --save-dev可以简写为-D.

查看包版本:

查看本地安装的包
D:\source_code\vue-book-master\chapter 10>npm ls babel-loader
chapter-10@1.0.0 D:\source_code\vue-book-master\chapter 10
`-- babel-loader@8.1.0
查看全局安装的包
D:\source_code\vue-book-master\chapter 10>npm ls babel-loader -g
D:\develop_soft\nodejs\node_global
`-- (empty)

三个命令:
1.查看npm服务器上所有的包版本信息
npm view babel-loader versions
2.查看该包最新的版本是哪一个
npm view babel-loader version
3.可以查看该包所有版本,以及更多的信息
npm info babel-loader
示例:

D:\source_code\vue-book-master\chapter 10>npm view babel-loader versions

[
  '4.0.0',         '4.1.0',        '4.2.0',         '4.3.0',
  '5.0.0',         '5.1.0',        '5.1.2',         '5.1.3',
  '5.1.4',         '5.2.0',        '5.2.1',         '5.2.2',
  '5.3.0',         '5.3.1',        '5.3.2',         '5.3.3',
  '5.4.0',         '5.4.1',        '5.4.2',         '6.0.0',
  '6.0.1',         '6.1.0',        '6.2.0',         '6.2.1',
  '6.2.2',         '6.2.3',        '6.2.4',         '6.2.5',
  '6.2.6',         '6.2.7',        '6.2.8',         '6.2.9',
  '6.2.10',        '6.3.0',        '6.3.1',         '6.3.2',
  '6.4.0',         '6.4.1',        '7.0.0-alpha.1', '7.0.0-alpha.2',
  '7.0.0-alpha.3', '7.0.0-beta.1', '7.0.0',         '7.1.0',
  '7.1.1',         '7.1.2',        '7.1.3',         '7.1.4',
  '7.1.5',         '8.0.0-beta.0', '8.0.0-beta.1',  '8.0.0-beta.2',
  '8.0.0-beta.3',  '8.0.0-beta.4', '8.0.0-beta.5',  '8.0.0-beta.6',
  '8.0.0',         '8.0.1',        '8.0.2',         '8.0.3',
  '8.0.4',         '8.0.5',        '8.0.6',         '8.1.0'
]


D:\source_code\vue-book-master\chapter 10>npm view babel-loader version
8.1.0

D:\source_code\vue-book-master\chapter 10>npm info babel-loader

babel-loader@8.1.0 | MIT | deps: 5 | versions: 64
babel module loader for webpack
https://github.com/babel/babel-loader

dist
.tarball: https://registry.npm.taobao.org/babel-loader/download/babel-loader-8.1.0.tgz
.shasum: c611d5112bd5209abe8b9fa84c3e4da25275f1c3

dependencies:
find-cache-dir: ^2.1.0 loader-utils: ^1.4.0   mkdirp: ^0.5.3         pify: ^4.0.1           schema-utils: ^2.6.5

maintainers:
- danez <daniel@tschinder.de>
- existentialism <bng412@gmail.com>
- hzoo <hi@henryzoo.com>
- loganfsmyth <loganfsmyth@gmail.com>

dist-tags:
latest: 8.1.0       next: 8.0.0-beta.6  old: 5.4.2

published 2 months ago by existentialism <bng412@gmail.com>

D:\source_code\vue-book-master\chapter 10>

15.介绍打包上线前,先来分析webpack打包后的产物有哪些。
本书所介绍和使用的都是单页面富应用(SPA)技术,这意味着最终只有一个html的文件,其余都是静态资源。实际部署到生产环境时,一般会将html 挂在后端程序下,由后端路由渲染这个页面,将页面所有的静态资源(css、js、image、icon、font等)单独部署到CDN,当然也可以和后端程序部署在一起,这样就实现了前后端完全分离。
我们在webpack的output 选项里已经指定了path 和 publicPath,打完包后,所有的资源都会保存在 demo/dist 目录下。
打包会用到下面两个依赖,使用NPM安装:
npm install --save-dev webpack-merge
npm install --save-dev html-webpack-plugin
为了方便开发和生产环境的切换,我们在demo目录下再新建一个用于生产环境的配置文件 webpack.prod.config.js。
编译打包,直接执行webpack 命令就可以。在package.json中,再加入一个build的快捷脚本用来打包:

"scripts" : {
"dev" : "webpack-dev-server --open --config webpack.config.js",
"build" : "webpack --progress --hide-modules --config webpack.prod.config.js"
}

先来看一下webpack.prod.config.js的代码:

var webpack = require('webpack');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var merge = require('webpack-merge');
var webpackBaseConfig = require('./webpack.config.js');
//清空基本配置的插件列表
webpackBaseConfig.plugins = [ ];
module.exports = merge(webpackBaseConfig,{
  output:{
    publicPath : '/dist/',
    //将入口文件重命名为带有20位hash值的唯一文件
    filename: '[name].[hash].js'
  },
  plugins:[
    new ExtractTextPlugin({
      // 提取 css,并重命名为带有20位hash值的唯一文件
      filename : '[name].[hash].css',
      allChunks : true
    }),
    // 定义当前node 环境为生产环境
    new webpack.DefinePlugin({
      'process.env' : {
        NODE_ENV : '"production"'
      }
    }),
    // 压缩js
    new webpack.optimize.UglifyJsPlugin({
      compress : {
        warnings : false
      }
    }),
    // 提取模板,并保存入口 html文件
    new HtmlWebpackPlugin({
      filename : '../index_prod.html',
      template : './index.ejs',
      inject : false
    })
  ]
});

上面安装的 webpack-merge 模块就是用于合并两个webpack 的配置文件,所以prod 的配置是在webpack.config.js 基础上扩展的。静态资源在大部分场景下都有缓存(304),更新上线后一般都希望用户能及时地看到内容,所以给打包后的css和js文件的名称都加了20位的hash值。这样文件名就唯一了(比如 main.b3dd20e2dae9d76af86b.js),只要不对html文件设置缓存,上线后立即就可以加载最新的静态资源。
html-webpack-plugin 是用来生成html 文件的,它通过template 选项来读取指定的模板 index.ejs,然后输出到filename 指定的目录,也就是demo/index_prod.html。模板 index.ejs 动态设置了静态资源的路径和文件名,代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>webpack App</title>
  <link rel="stylesheet" href="<%= htmlwebpackPlugin.files.css[0] %>">
</head>
<body>
  <div id="app"></div>
  <script type="text/javascript" src="<%= htmlwebpackPlugin.files.js[0] %>"></script>
</body>
</html>

最后在终端运行 npm run build,等待一会就会完成打包,成功后在demo下会生成一个dist的目录,里面就是打包完的所有静态资源。

ejs 是一个JavaScript 模板库,用来从JSON 数据中生成HTML 字符串,常用于 Node.js。

webpack除了上述内容外,和有很多高级的配置和丰富的插件及加载器,可查阅webpack文档:https://webpack.js.org/

本章代码:https://github.com/icarusion/vue-book
vue-book 下的demo目录下执行 npm install 命令会自动安装所有的依赖,然后执行 npm run dev 启动服务。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354