webpack+vue2.0+nodeJs搭建环境

npm 和 nodejs还有淘宝镜像


npm 的全称是 nodejs包管理,现在越来越多的项目(包)都可以通过npm来安装管理,nodejs是js运行在服务器端的平台,它使得js的能力进一步提高,我们还要使用nodejs配合 webpack 来完成热加载的功能。所以读者最好有nodejs的开发经验,如果有express的经验更好。


让我们一步一步从零搭建这个项目



首先新建一个目录,名为 myProject ,这是我们的项目目录。然后执行一些基本的步骤,比如 npm init 命令,在我们的项目中生成 package.json 文件,这几乎是必选的,因为我们的项目要有很多依赖,都是通过npm来管理的,而npm对于我们项目的管理,则是通过package.json文件:


执行npm init之后,会提示你填写一些项目的信息,一直回车默认就好了,或者直接执行 npm init -y 直接跳过询问步骤
然后我们新建一个叫做 app 的目录,这个是我们页面模块的目录,再在app目录下建立一个index目录,假设这个是首页模块的目录,然后再在index目录下建立一个 index.html 文件和 index.js 文件,分别是首页入口html文件和主js文件,然后再在index目录下建立一个components目录,这个目录用作存放首页组件模块的目录,因为我们最终要实现组件化开发。这样,当你完成上面的步骤后,你的项目看上去应该是这样的:

接下来通过npm安装项目依赖项(因为我安装了淘宝镜像,所以install的时候,用的是cnpm,没有安装淘宝镜像的可以用npm来下载依赖包):

cnpm install webpack webpack-dev-server vue-loader vue-html-loader css-loader vue-style-loader vue-hot-reload-api babel-loader babel-core babel-plugin-transform-runtime babel-preset-es2015 babel-runtime@5 --save-dev

下载完应该是这样的:

这个时候,你的package.json文件看起来应该是这样的:

我们安装了 babel 一系列包,用来解析ES6语法,因为我们使用ES6来开发项目,如果你不了解ES6语法,建议你看一看阮老师的教程,然后我们安装了一些loader包,比如css-loader/vue-loader等等,因为webpack是使用这些指定的loader去加载指定的文件的。

下来安装vue

cnpm install vue --save-dev

另外我们还使用 npm install vue –save 命令安装了 vue ,这个就是我们要在项目中使用的vue.js,我们可以直接像开发nodejs应用一样,直接require(‘vue’);即可,而不需要通过script标签引入,这一点在开发中很爽。

安装完了依赖,编辑以下文件并保存到相应位置:

1、index.html文件:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no">
    <meta charset="utf-8">
    <title>首页</title>
</head>
<body>
<!-- vue的组件以自定义标签的形式使用 -->
<favlist></favlist>
</body>
</html>

2、index.js文件

import Vue from 'Vue'
import Favlist from './components/Favlist.vue'
Vue.config.debug = true;//开启错误提示
window.onload = function () {
    new Vue({
        el: '#app',
        components: {
            'my-component': Favlist
        }
    });
}


3、在components目录下新建一个 Favlist.vue 文件,作为我们的第一个组件:

<template>
    <div v-for="n in 10">div</div>
</template>

<script>
    export default {
        data () {
            return {
                msg: 'Hello World!'
            }
        }
    }
</script>

<style>
    html{
        background: red;
    }
</style>

要看懂上面的代码,你需要了解vue.js,假如你看不懂也没关系,我们首先在index.html中使用了自定义标签(即组件),然后在index.js中引入了Vue和我们的Favlist.vue组件,Favlist.vue文件中,我们使用了基本的vue组件语法,最后,我们希望它运行起来,这个时候,我们就需要webpack了。

在项目目录下新建 build 目录,用来存放我们的构建相关的代码文件等,然后在build目录下新建 webpack.config.js 这是我们的webpack配置文件,webpack需要通过读取你的配置,进行相应的操作,类似于gulpfile.js或者gruntfile.js等。

webpack.config.js

// nodejs 中的path模块
var path = require('path');

module.exports = {
    // 入口文件,path.resolve()方法,可以结合我们给定的两个参数最后生成绝对路径,最终指向的就是我们的index.js文件
    entry: path.resolve(__dirname, '../app/index/index.js'),
    // 输出配置
    output: {
        // 输出路径是 myProject/output/static
        path: path.resolve(__dirname, '../output/static'),
        publicPath: 'static/',
        filename: '[name].[hash].js',
        chunkFilename: '[id].[chunkhash].js'
    },
    resolve: {
        extensions: ['*', '.js', '.vue']
    },
    // 特别注意:webpack v1 和webpack v2 的区别
    module: {
        rules: [
            /* 用来解析vue后缀的文件 */
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            /* 用babel来解析js文件并把es6的语法转换成浏览器认识的语法 */
            {
                test: /\.js$/,
                loader: 'babel-loader',
                /* 排除模块安装目录的文件 */
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true
                        }
                    },
                ]
            }
        ]
    }
}

 

上例中,相信你已经看懂了我的配置,入口文件是index.js文件,配置了相应输出,然后使用 vue-loader 去加载 .vue 结尾的文件,接下来我们就可以构建项目了,我们可以在命令行中执行:

webpack --display-modules --display-chunks --config build/webpack.config.js

通过webpack命令,并且通过 –config 选项指定了我们配置文件的位置是 ‘build/webpack.config.js’,并通过 –display-modules 和 –display-chunks 选项显示相应的信息。如果你执行上面的命令,可能得到下图的错误:


错误提示我们应该选择合适的loader去加载这个 ‘./app/index/index.js’ 这个文件,并且说不期望index.js文件中的标识符(Unexpected token),这是因为我们使用了ES6的语法 import 语句,所以我们要使用 babel-loader 去加载我们的js文件,在配置文件中添加一个loaders项目,如下:

// nodejs 中的path模块
var path = require('path');

module.exports = {
    // 入口文件,path.resolve()方法,可以结合我们给定的两个参数最后生成绝对路径,最终指向的就是我们的index.js文件
    entry: path.resolve(__dirname, '../app/index/index.js'),
    // 输出配置
    output: {
        // 输出路径是 myProject/output/static
        path: path.resolve(__dirname, '../output/static'),
        publicPath: 'static/',
        filename: '[name].[hash].js',
        chunkFilename: '[id].[chunkhash].js'
    },
    module: {

        loaders: [
            // 使用vue-loader 加载 .vue 结尾的文件
            {
                test: /\.vue$/,
                loader: 'vue'
            },
            {
                test: /\.js$/,
                loader: 'babel?presets=es2015',
                exclude: /node_modules/
            }
        ]
    }
}


可是运行还是会得到如下错误提示:


这个错误提示说的是can't resolve 'babel',然后你需要访问https://webpack.js.org/guides/migrating/#automatic-loader-module-name-extension-removed这个网站


-loader引用装载机时不再可能省略扩展:

于是更改了loader

// nodejs 中的path模块
var path = require('path');

module.exports = {
    // 入口文件,path.resolve()方法,可以结合我们给定的两个参数最后生成绝对路径,最终指向的就是我们的index.js文件
    entry: path.resolve(__dirname, '../app/index/index.js'),
    // 输出配置
    output: {
        // 输出路径是 myProject/output/static
        path: path.resolve(__dirname, '../output/static'),
        publicPath: 'static/',
        filename: '[name].[hash].js',
        chunkFilename: '[id].[chunkhash].js'
    },
    resolve: {
        extensions: ['*', '.js', '.vue']
    },
    // 特别注意:webpack v1 和webpack v2 的区别
    module: {
        rules: [
            /* 用来解析vue后缀的文件 */
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            /* 用babel来解析js文件并把es6的语法转换成浏览器认识的语法 */
            {
                test: /\.js$/,
                loader: 'babel-loader',
                /* 排除模块安装目录的文件 */
                exclude: /node_modules/
            }
        ]
    }
}

 

然后运行一下webpack

sorry,webpack --display-modules --display-chunks --config build/webpack.config.js运行webpack以后还是报错,得到如下的错误

 Cannot find module 'vue-template-compiler'

所以就需要安装一下这个'vue-template-compiler';

cnpm install vue-template-compiler --save-dev

然后再运行一下webpack。

webpack --display-modules --display-chunks --config build/webpack.config.js


此时出现了问题,我把项目文件里的node_modules删除,重新下载这些依赖包,重新下载依赖包,直接再命令行里npm install 就行了

npm install

再运行webpack

webpack --display-modules --display-chunks --config build/webpack.config.js


然后就成功了

但是虽然编译成功了,但是 vue的坑又来了

再index.html页面上引入打包后的js

然后打开页面,开始报错main.04065ac2ebf0309c20dd.js:881 [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

令人百思不得其解的是,如果不使用 webpack 打包,而是直接在 HTML 文件中使用 script 标签引入 Favlist.js,在 JavaScript 中手写 MyComponent 的组件选项模板,完成的页面却可以正确显示,为什么这里会提示“模板或渲染函数未定义”呢?

这其实与 Vue 的两种不同的构建有关,在上述使用 webpack 打包的项目需要使用独立构建的 Vue 库,而在 node_modules/vue/package.json 文件中,已经通过 main 属性指定了通过 import Vue from ‘vue’ 或 require(‘vue’) 所引入的文件是 dist/vue.runtime.common.js,即运行时构建的 Vue 库。直接在 HTML 文件中使用 script 标签引入的是独立构建的 Vue 库,因此没有问题。

在包含单文件组件的项目中,使用 webpack 打包时已经将单文件组件中的模板预先编译成了渲染函数,因此一般情况下使用运行时构建的 Vue 库就可以了,但如果在使用 new Vue 创建 Vue 的根实例时,模板是从 el 挂载元素提取的,则需要使用独立构建的 Vue 库。

在使用 script 标签引入 Vue.js 的项目中,任意实例选项或组件选项中包含了 template 模板属性或从 el 挂载元素提取的模板时,均需要使用独立构建的 Vue 库。

要解决本文最开始的问题,需要在 webpack 配置中的 resolve 属性对象中添加如下 alias 设置:

resolve: {
        extensions: ['*', '.js', '.vue'],
        alias: {
            'vue$': 'vue/dist/vue.common.js'
         }

    },

这里的 vue$ 表示精确匹配,由于 index.js 中还有一处大小写错误 import Vue from ‘Vue’,因此需要将 from 后面的 ‘Vue’ 修改为小写的 ‘vue’ 之后页面才能正确显示。

现在的webpack.config.js是这样的

// nodejs 中的path模块
var path = require('path');

module.exports = {
    // 入口文件,path.resolve()方法,可以结合我们给定的两个参数最后生成绝对路径,最终指向的就是我们的index.js文件
    entry: path.resolve(__dirname, '../app/index/index.js'),
    // 输出配置
    output: {
        // 输出路径是 myProject/output/static
        path: path.resolve(__dirname, '../output/static'),
        publicPath: 'static/',
        filename: '[name].[hash].js',
        chunkFilename: '[id].[chunkhash].js'
    },
    resolve: {
        extensions: ['*', '.js', '.vue'],
        alias: {
            'vue$': 'vue/dist/vue.common.js'
         }

    },
    // 特别注意:webpack v1 和webpack v2 的区别
    module: {
        rules: [
            /* 用来解析vue后缀的文件 */
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            /* 用babel来解析js文件并把es6的语法转换成浏览器认识的语法 */
            {
                test: /\.js$/,
                loader: 'babel-loader',
                /* 排除模块安装目录的文件 */
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true
                        }
                    },
                ]
            }
        ]
    }
}


index.js是这样的,把import Vue from 'Vue'    from 后面的 ‘Vue’ 修改为小写的 ‘vue’ 之后页面才能正确显示。

import Vue from 'vue'
import Favlist from './components/Favlist'
Vue.config.debug = true;//开启错误提示
window.onload = function () {
    new Vue({
        el: '#app',
        components: {
            'my-component': Favlist
        }
    });
}

重新运行一下webpack,然后重新再index中引入打包后的js,运行页面,就会

然后用浏览器打开这个页面,你可以看到你写的代码正确的执行了。

那么问题来了,难道我们每次都要手动的引入输出的js文件吗?因为每次构建输出的js文件都带有 hash 值,如 main.ce853b65bcffc3b16328.js,就不能更智能一点吗?每次都自动写入?怎么会不可能,否则这东西还能火吗,要实现这个功能,我们就要使用webpack的插件了,html-webpack-plugin插件,这个插件可以创建html文件,并自动将依赖写入html文件中。

首先安装 html-webpack-plugin 

cnpm install html-webpack-plugin --save-dev


然后修改配置项

// nodejs 中的path模块
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    // 入口文件,path.resolve()方法,可以结合我们给定的两个参数最后生成绝对路径,最终指向的就是我们的index.js文件
    entry: path.resolve(__dirname, '../app/index/index.js'),
    // 输出配置
    output: {
        // 输出路径是 myProject/output/static
        path: path.resolve(__dirname, '../output/static'),
        publicPath: 'static/',
        filename: '[name].[hash].js',
        chunkFilename: '[id].[chunkhash].js'
    },
    resolve: {
        extensions: ['*', '.js', '.vue'],
        alias: {
            'vue$': 'vue/dist/vue.common.js'
         }

    },
    // 特别注意:webpack v1 和webpack v2 的区别
    module: {
        rules: [
            /* 用来解析vue后缀的文件 */
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            /* 用babel来解析js文件并把es6的语法转换成浏览器认识的语法 */
            {
                test: /\.js$/,
                loader: 'babel-loader',
                /* 排除模块安装目录的文件 */
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true
                        }
                    },
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            filename: '../index.html',
            template: path.resolve(__dirname, '../app/index/index.html'),
            inject: true
        })
    ]
}

然后再次执行构建命令,成功之后,看你的输出目录,多出来一个index.html文件,双击它,代码正确执行,你可以打开这个文件查看一下,webpack自动帮我们引入了相应的文件。

问题继续来了,难道每次我们都要构建之后才能查看运行的代码吗?那岂不是很没有效率,别担心,webpack提供了几种方式,进行热加载,在开发模式中,我们使用这种方式来提高效率,这里要介绍的,是使用 webpack-dev-middleware中间件和webpack-hot-middleware中间件,首先安装两个中间件:

cnpm install webpack-dev-middleware webpack-hot-middleware --save-dev

另外,还要安装express,这是一个nodejs框架

cnpm install express --save-dev


先简单介绍一下这俩个插件

我们之前所面临的问题是,如果我们的代码改动了,我们要想看到浏览器的变化,需要先对项目进行构建,然后才能查看效果,这样对于开发效率来讲,简直就是不可忍受的一件事,试想我仅仅修改一个背景颜色就要构建一下项目,这肯定太繁琐了,好在有webpack-dev-middleware中间件,它是对webpack一个简单的包装,它可以通过连接服务器服务那些从webpack发射出来的文件,它有一下几点好处:

1、不会向硬盘写文件,而是在内存中,注意我们构建项目实际就是向硬盘写文件。

2、当文件改变的时候,这个中间件不会再服务旧的包,你可以直接刷新浏览器就能看到最新的效果,这样你就不必等待构建的时间,所见即所得。

下面我们在build目录中创建一个 dev-server.js 的文件,并写入一下内容:

// 引入必要的模块
var express = require('express')
var webpack = require('webpack')
var config = require('./webpack.config')

// 创建一个express实例
var app = express()

// 调用webpack并把配置传递过去
var compiler = webpack(config)

// 使用 webpack-dev-middleware 中间件
var devMiddleware = require('webpack-dev-middleware')(compiler, {
    publicPath: config.output.publicPath,
    stats: {
        colors: true,
        chunks: false
    }
})

// 注册中间件
app.use(devMiddleware)

// 监听 8888端口,开启服务器
app.listen(8888, function (err) {
    if (err) {
        console.log(err)
        return
    }
    console.log('Listening at http://localhost:8888')
})

然后运行启动一下nodeJs

node build/dev-server.js

如果看到下图所示,证明你的服务成功开启了:

接下来打开浏览器,输入:

http://localhost:8888/app/index/index.html

如果不出意外,你会得到一个404,如下图:


我们要对我们的 webpack.config.js 配置文件做两处修改:

1、将 config.output.publicPath 修改为 ‘/‘:

    // 输出配置
    output: {
        // 输出路径是 myProject/output/static
        path: path.resolve(__dirname, '../output/static'),
        publicPath: '/',
        filename: '[name].[hash].js',
        chunkFilename: '[id].[chunkhash].js'
    },

2、将 plugins 中 HtmlWebpackPlugin 中的 filename 修改为 ‘app/index/index.html’
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'app/index/index.html',
            template: path.resolve(__dirname, '../app/index/index.html'),
            inject: true
        })
    ]

重启服务,再刷新页面,如果看到如下界面,证明你成功了:

node build/dev-server.js

但是这样开发模式下的确是成功了,可是我们直接修改了 webpack.config.js 文件,这就意味着当我们执行 构建命令 的时候,配置变了,那么我们的构建也跟着变了,所以,一个好的方式是,不去修改webpack.config.js文件,我们在build目录下新建一个 webpack.dev.conf.js文件,意思是开发模式下要读取的配置文件,并写入一下内容:
var HtmlWebpackPlugin = require('html-webpack-plugin')
var path = require('path');
// 引入基本配置
var config = require('./webpack.config');

config.output.publicPath = '/';

config.plugins = [
    new HtmlWebpackPlugin({
        filename: 'app/index/index.html',
        template: path.resolve(__dirname, '../app/index/index.html'),
        inject: true
    })
];

module.exports = config;

这样,我们在dev环境下的配置文件中覆盖了基本配置文件,我们只需要在dev-server.js中将

var config = require('./webpack.config')

修改成
var config = require('./webpack.dev.conf')

然后,重启服务,刷新浏览器,你应该得到同样的成功结果,而这一次当我们执行构建命令:

webpack --display-modules --display-chunks --config build/webpack.config.js

并不会影响构建输出,因为我们没有直接修改webpack.config.js文件。

现在我们已经使用 webpack-dev-middleware 搭建基本的开发环境了,但是我们并不满足,因为我们每次都要手动去刷新浏览器,所谓的热加载,意思就是说能够追踪我们代码的变化,并自动更新界面,甚至还能保留程序状态。要完成热加载,我们就需要使用另外一个中间件 webpack-hot-middleware

2、webpack-hot-middleware

webpack-hot-middleware 只配合 webpack-dev-middleware 使用,它能给你提供热加载。

它的使用很简单,总共分4步:

1、安装,我们上面已经安装过了
2、在 webpack.dev.conf.js 配置文件中添加三个插件,如下:

var HtmlWebpackPlugin = require('html-webpack-plugin')
var path = require('path');
var webpack = require('webpack');
// 引入基本配置
var config = require('./webpack.config');

config.output.publicPath = '/';

config.plugins = [
    // 添加三个插件
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),

    new HtmlWebpackPlugin({
        filename: 'app/index/index.html',
        template: path.resolve(__dirname, '../app/index/index.html'),
        inject: true
    })
];

module.exports = config;


3、在 webpack.config.js 文件中入口配置中添加 ‘webpack-hot-middleware/client’,如下:

entry: ['webpack-hot-middleware/client', path.resolve(__dirname, '../app/index/index.js')],

4、在 dev-server.js 文件中使用插件

// 引入必要的模块
var express = require('express')
var webpack = require('webpack')
var config = require('./webpack.dev.conf')

// 创建一个express实例
var app = express()

// 调用webpack并把配置传递过去
var compiler = webpack(config)

// 使用 webpack-dev-middleware 中间件
var devMiddleware = require('webpack-dev-middleware')(compiler, {
    publicPath: config.output.publicPath,
    stats: {
        colors: true,
        chunks: false
    }
})

// 使用 webpack-hot-middleware 中间件
var hotMiddleware = require('webpack-hot-middleware')(compiler)

// 注册中间件
app.use(devMiddleware)
// 注册中间件
app.use(hotMiddleware)

// 监听 8888端口,开启服务器
app.listen(8888, function (err) {
    if (err) {
        console.log(err)
        return
    }
    console.log('Listening at http://localhost:8888')
})

但是启动服务的时候会报错


使用webpack命令的时候报错,Webpack.optimize.OccurenceOrderPlugin is not a constructor

解决方法:OccurenceOrderPlugin更改为OccurrenceOrderPlugin 即可

将webpack.dev.conf.js中的OccurenceOrderPlugin更改为OccurrenceOrderPlugin 即可;

var HtmlWebpackPlugin = require('html-webpack-plugin')
var path = require('path');
var webpack = require('webpack');
// 引入基本配置
var config = require('./webpack.config');

config.output.publicPath = '/';

config.plugins = [
    // 添加三个插件
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),

    new HtmlWebpackPlugin({
        filename: 'app/index/index.html',
        template: path.resolve(__dirname, '../app/index/index.html'),
        inject: true
    })
];

module.exports = config;

ok,现在重启的服务,然后修改 Favlist.vue 中的color为 ‘green’,不用启动服务也不用刷新页面,直接就可以看到效果


那么这样就完美了吗?还没有,如果你细心,你会注意到,我们上面在第2步中修改了 webpack.config.js 这个基本配置文件,修改了入口配置,如下:

    // 入口文件,path.resolve()方法,可以结合我们给定的两个参数最后生成绝对路径,最终指向的就是我们的index.js文件
    entry: ['webpack-hot-middleware/client', path.resolve(__dirname, '../app/index/index.js')],


这也会导致我们之前讨论过的问题,就是会影响构建,所以我们不要直接修改 webpack.config.js 文件,我们还是在 webpack.dev.conf.js 文件中配置,如下:

var HtmlWebpackPlugin = require('html-webpack-plugin')
var path = require('path');
var webpack = require('webpack');
// 引入基本配置
var config = require('./webpack.config');

config.output.publicPath = '/';

config.plugins = [
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    new HtmlWebpackPlugin({
        filename: 'app/index/index.html',
        template: path.resolve(__dirname, '../app/index/index.html'),
        inject: true
    })
];

// 动态向入口配置中注入 webpack-hot-middleware/client
var devClient = 'webpack-hot-middleware/client';
Object.keys(config.entry).forEach(function (name, i) {
    var extras = [devClient]
    config.entry[name] = extras.concat(config.entry[name])
})

module.exports = config;

但是我们还是要讲 webpack.config.js 文件中的入口配置修改为多入口配置的方式,这个修改不会影响构建,所以无所谓:

// 入口文件,path.resolve()方法,可以结合我们给定的两个参数最后生成绝对路径,最终指向的就是我们的index.js文件
    entry: {
        index: path.resolve(__dirname, '../app/index/index.js')
    },

再次查看浏览器,发现可以热加载。但是这样就结束了吗?还没有,修改了 index.html 文件,看看会不会热加载,实际上不会,你还是需要手动刷新页面,为了能够当 index.html 文件的改动也能够触发自动刷新,我们还需要做一些工作。

第一步:在 dev-server.js 文件中监听html文件改变事件,修改后的 dev-server.js 文件如下:

// 引入必要的模块
var express = require('express')
var webpack = require('webpack')
var config = require('./webpack.dev.conf')

// 创建一个express实例
var app = express()

// 调用webpack并把配置传递过去
var compiler = webpack(config)

// 使用 webpack-dev-middleware 中间件
var devMiddleware = require('webpack-dev-middleware')(compiler, {
    publicPath: config.output.publicPath,
    stats: {
        colors: true,
        chunks: false
    }
})

var hotMiddleware = require('webpack-hot-middleware')(compiler)

// webpack插件,监听html文件改变事件
compiler.plugin('compilation', function (compilation) {
    compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
        // 发布事件
        hotMiddleware.publish({ action: 'reload' })
        cb()
    })
})

// 注册中间件
app.use(devMiddleware)
// 注册中间件
app.use(hotMiddleware)

// 监听 8888端口,开启服务器
app.listen(8888, function (err) {
    if (err) {
        console.log(err)
        return
    }
    console.log('Listening at http://localhost:8888')
})


从上面的代码上看,我们增加了如下的代码

// webpack插件,监听html文件改变事件
compiler.plugin('compilation', function (compilation) {
    compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
        // 发布事件
        hotMiddleware.publish({ action: 'reload' })
        cb()
    })
})

这段代码可能看不懂。涉及到webpack的插件编写,可以参考github上的代码

https://github.com/webpack/docs/wiki/plugins

https://github.com/webpack/docs/wiki/How-to-write-a-plugin

第二步:修改 webpack.dev.conf.js 文件如下:

var HtmlWebpackPlugin = require('html-webpack-plugin')
var path = require('path');
var webpack = require('webpack');
// 引入基本配置
var config = require('./webpack.config');

config.output.publicPath = '/';

config.plugins = [
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    new HtmlWebpackPlugin({
        filename: 'app/index/index.html',
        template: path.resolve(__dirname, '../app/index/index.html'),
        inject: true
    })
];

// var devClient = 'webpack-hot-middleware/client';
var devClient = './build/dev-client';
Object.keys(config.entry).forEach(function (name, i) {
    var extras = [devClient]
    config.entry[name] = extras.concat(config.entry[name])
})

module.exports = config;

我们修改了devClient变量,将 ‘webpack-hot-middleware/client’ 替换成 ‘./build/dev-client’,最终会导致,我们入口配置会变成下面这样:

entry: {
    index: [
        './build/dev-client',
        path.resolve(__dirname, '../app/index/index.js')
    ]
},
第三步:新建 build/dev-client.js 文件,并编辑如下内容:
var hotClient = require('webpack-hot-middleware/client')

// 订阅事件,当 event.action === 'reload' 时执行页面刷新
hotClient.subscribe(function (event) {
    if (event.action === 'reload') {
        window.location.reload()
    }
})

现在重新启动服务,然后去改变app/index/index.html文件的时候,页面就会自动刷新

开发环境终于搞定了,下面我们再来谈一谈生产环境,也就是构建输出,我们现在可以执行一下构建命令,看看输出的内容是什么,为了不必每次都要输入下面这条长命令:

webpack --display-modules --display-chunks --config build/webpack.config.js

我们在 package.json 文件中添加 “scripts” 项,如下图:

 

  "scripts": {
//    "test": "echo \"Error: no test specified\" && exit 1"
    "build":"webpack --display-modules --display-chunks --config build/webpack.config.js",
    "dev":"node ./build/dev-server.js"
  },

这样,我们就可以通过执行下面命令来进行构建,同时我们还增加了一条开启开发服务器的命令:

// 构建
npm run build
// 开启开发服务器
npm run dev

现在我们只有一个js文件输出了,并没有css文件输出,在生产环境,我们希望css文件生成单独的文件,所以我们要使用 extract-text-webpack-plugin 插件,安装:

cnpm install extract-text-webpack-plugin --save-dev

然后在build目录下新建 webpack.prod.conf.js 文件,顾名思义,这个使我们区别于开发环境,用于生产环境的配置文件,并编辑一下内容:

var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var path = require('path');
var webpack = require('webpack');
// 引入基本配置
var config = require('./webpack.config');

config.vue = {
    loaders: {
        css: ExtractTextPlugin.extract("css")
    }
};

config.plugins = [
    // 提取css为单文件
    new ExtractTextPlugin("../[name].[contenthash].css"),

    new HtmlWebpackPlugin({
        filename: '../index.html',
        template: path.resolve(__dirname, '../app/index/index.html'),
        inject: true
    })
];

module.exports = config;

上面的代码中,我们覆盖了 webpack.config.js 配置文件的 config.plugins 项,并且添加了 config.vue 项.

然后修改 package.json 文件中的 script 项为如下:

  "scripts": {
    "build": "webpack --display-modules --display-chunks --config build/webpack.prod.conf.js",
    "dev": "node ./build/dev-server.js"
  },


另外我们还可以添加如下插件在我们的 webpack.prod.conf.js 文件中,作为生产环境使用:

config.plugins = [
    // 提取css为单文件
    new ExtractTextPlugin("../[name].[contenthash].css"),

    new HtmlWebpackPlugin({
        filename: '../index.html',
        template: path.resolve(__dirname, '../app/index/index.html'),
        inject: true
    }),
    new webpack.DefinePlugin({
        'process.env': {
            NODE_ENV: '"production"'
        }
    }),
    // 压缩代码
    new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        }
    }),
    new webpack.optimize.OccurenceOrderPlugin(),
    // 提取css为单文件
    new ExtractTextPlugin("../[name].[contenthash].css"),
    new HtmlWebpackPlugin({
        filename: '../index.html',
        template: path.resolve(__dirname, '../app/index/index.html'),
        inject: true
    })

];


到这里实际上搭建的已经差不多了,唯一要做的就是完善,比如公共模块的提取,如何加载图片,对于第一个问题,如何提取公共模块,我们可以使用 CommonsChunkPlugin 插件,在 webpack.prod.conf.js 文件中添加如下插件:

new webpack.optimize.CommonsChunkPlugin({
    name: 'vendors',
    filename: 'vendors.js',
}),

然后在 webpack.config.js 文件中配置入口文件:
    // 入口文件,path.resolve()方法,可以结合我们给定的两个参数最后生成绝对路径,最终指向的就是我们的index.js文件
    entry: {
        index: path.resolve(__dirname, '../app/index/index.js'),
        vendors: [
            'Vue'
        ]
    },    

上面代码的意思是,我们把Vue.js当做公共模块单独打包,你可以在这个数组中增加其他模块,一起作为公共模块打包成一个文件,我们执行构建命令,然后查看输出,如下图,成功提取:



对于加载图片的问题,我们知道,webpack的哲学是一切皆是模块,然后通过相应的loader去加载,所以加载图片,我们就需要使用到 url-loader,在webpack.config.js 文件中添加一个loader配置:

   // 特别注意:webpack v1 和webpack v2 的区别
    module: {
        rules: [
            /* 用来解析vue后缀的文件 */
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            /* 用babel来解析js文件并把es6的语法转换成浏览器认识的语法 */
            {
                test: /\.js$/,
                loader: 'babel-loader',
                /* 排除模块安装目录的文件 */
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true
                        }
                    },
                ]
            },
            // 加载图片
            {
                test: /\.(png|jpg|gif|svg)$/,
                loader: 'url',
                query: {
                    limit: 10000,
                    name: '[name].[ext]?[hash:7]'
                }
            }
        ]
    },

项目构建参照https://www.cnblogs.com/linfangshuhellowored/p/5657285.html




欢迎关注我的个人技术公众号!javascript艺术











Logo

前往低代码交流专区

更多推荐