使用 Webpack 来构建 Vue 项目

使用 Webpack 来构建 Vue 项目

在本章当中,我们不使用官方提供的 Vue-Cli,而是从头开始搭建,一步一步实现一个简易脚手架,理解 Vue-Cli 到底是怎么实现的(顺路了解一下 WebpackVue 项目中的使用)

源码可以见 基于 vue-cli 整合的一个个人脚手架

需要注意的是,WebpackVue 的版本均在 2.0 以上

创建目录

大致目录结构如下

1
2
3
4
5
├── index.html         ==> 根本件,最后用于展示 App.vue 组件
├── main.js ==> 入口文件
├── App.vue ==> vue 组件
├── pacjage.json ==> 工程配置文件(依赖,配置)
└── webpack.config.js ==> webpack 配置文件

首先新建一个 HTML 文件,添加一个 idbox 的容器

1
2
// html
<div id='box'></div>

然后在 main.js 文件中添加一点内容,用于最后的渲染

1
2
3
4
5
6
7
8
9
// main.js
import Vue from 'vue'
import App from './App.vue'

new Vue({
el: '#box',
// 关于 h => h(App) 见最下方
render: h => h(App)
})

接下来来安装 Webpackwebpack-dev-server,再配置一下 Webpack,先简单的指定一下入口和出口文件

1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js
module.exports = {
// 入口文件
entry: './main.js',

// 出口文件
output: {
filename: 'bundle.js',
// 当前路径
path: __dirname
}
};

然后就可以在我们的 index.html 中引入出口文件中指定的 JavaScript

  • 这里是 bundle.js,但是目录中不使用 webpack -p 的话是不存在这个文件的
  • Webpack 会在编译执行的时候为我们自动生成该文件
1
2
// index.html
<script src='./bundle.js'></script>

关于出口文件,如果想指定输出的目录,可以按照下面这样来设置

1
2
3
4
5
6
7
8
9
10
11
12
output: {

// 如果目录,如果不存在则会去新建
path: path.resolve(__dirname, './src'),

// 设置静态目录(可以从这个目录下直接进行读取)
publicPath: '/src/',

// 文件名称
filename: 'bundle.js'

},

配置 vue-loader

如果需要解析像 App.vue 这样的文件,使其变成正常可以访问的代码,我们需要引入 vue-loader 来处理,这个时候我们就可以在 Webpack 中配置我们的相关 loader 了(这也是 Webpack 最核心的东西),在 webpack.config.js 中添加

1
2
3
4
5
6
7
8
9
10
11
module: {
// 所有的 loader 都配置在这里
rules: [
// 下面这句的意思是 使用 vue-loader 来解析处理以 .vue 结尾的文件
// 如果多个 loader 的话,中间可以使用 ! 来连接,比如 style!css
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
}

配置完 Webpack 以后就可以来安装 vue-loader 以及其相关的一些组件了,单单使用 vue-loader 编译写入 .vue 文件是不够的,因为在 .vue 文件当中主要分为三大块,如下所示

1
2
3
4
5
<template></template>

<script></script>

<style></style>

每一部分都需要相对应的 loader 来进行处理,这时就需要同时安装以下 loader

1
2
3
4
5
6
7
8
9
// 处理模版
vue-template-compiler ==> 编译 vue 的 template 部分(之前使用的是 vue-html-loader)

// 处理 css,因为 css 分为 .css 文件和行内样式,需要同时加载
css-loader ==> 编译写入 css
vue-style-loader ==> 编译 vue 的样式部分

// 处理 js,关于 babel 部分见下方
vue-hot-reload-api ==> 使 webpack 对 vue 实现热替换

可以使用以下命令来进行安装

1
npm install vue-template-compiler css-loader vue-style-loader vue-hot-reload-api --save-dev

配置 babel

众所周知,现在浏览器对 ES6 语法的支持还不是很完善,这个时候也就需要使用 Babel 来解析 ES6 语法,先来配置 Webpack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module: {
rules: [
// 规则和上面类似,需要注意的就是多了一个 exclude
// 意思是 匹配除了 /node_modules/ 和 bower_components 之外的所有 .js 文件
// 关于 options 选项,如果在根目录下的 .babelrc 配置了,这里就不需要了,两者是一样的
// 关于 .babelrc 见最后
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
// 先配置一个前置
presets: ['es2015'],
// 还需要一个插件来使每次改变的时候,实时编译
plugins: ['transform-runtime']
}
}
}

]
}

Babel 需要的相关组件如下

1
2
3
4
5
6
7
8
9
babel-loader                    ==> 主程序

babel-core ==> 编译核心(语法层面)

babel-plugin-transform-runtime ==> 实时编译插件

babel-perset-es2015 ==> es6 语法

babel-runtime ==> 上面几个程序需要这个组件来运行(babel 执行环境)

最后来汇总一下所有涉及到的依赖

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
vue-loader                      ==> 编译写入 .vue 文件

// 处理 .vue 中的模版
vue-template-compiler ==> 编译 vue 的 template 部分,(之前使用的是 vue-html-loader)

// 处理 css,因为 css 分为 .css 文件和行内样式,需要同时加载
css-loader ==> 编译写入 css
vue-style-loader ==> 编译 vue 的样式部分

// 处理 js
vue-hot-reload-api ==> 使 webpack 对 vue 实现热替换

// 使用 babel 来处理 es6 语法
babel-loader ==> 主程序
babel-core ==> 编译核心(语法层面)
babel-plugin-transform-runtime ==> 实时编译插件
babel-perset-es2015 ==> es6 语法
babel-runtime ==> 上面几个 babel 程序需要这个组件来运行(babel 执行环境)

// 处理文件和图片
file-loader ==> 用于打包文件和图片,默认情况下生成文件的文件名是文件名与 md5 哈希值的组合

// webpack
webpack ==> webpack 主程序
webpack-dev-server ==> webpack 执行环境

// vue
vue ==> 这个就不用多说了

// 可选
url-loader ==> 实际上是对 file-loader 的封装(见最后)
webpack-merge ==> 开发环境和生产环节的 webpaak 配置文件的配置合并

启动

安装和配置完成以后,这个时候就可以来启动程序了,但是如果想在命令行直接使用类似 npm run dev 命令的话,就需要配置一下 package.json 文件,在 scripts 标签下设置

1
2
3
4
5
6
7
// 设置 scripts 即可
// --inline 自动刷新
// --hot 热载
// --port 设定端口(在端口被占用的情况下可以利用这个参数来指定,默认的为 8080)
'scripts': {
'dev': 'webpack-dev-server --inline --hot'
}

然后就可以使用 npm run dev 命令来启动服务了,如果在控制台中看到

1
Project is running at http://localhost:8080/

这个时候就表明启动成功了,可以访问 http://localhost:8080/ 来查看效果了,如果配置全部正确的话,就可以在浏览器中看到对应的内容了,修改内容直接保存,浏览器也会自动跟着刷新,如果在最后运行的时候发现控制台出现如下内容

1
2
3
4
5
[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.

// ...

这个就主要涉及到了 Vue 的运行时构建和独立构建的区别,简单来说,就是在独立构建的时候可以使用用 template 选项,而在运行时构建则不行,只能通过模板来编译转成 render 函数,两者区别如下

  • 独立构建包含模板编译器,运行时构建不包含模板编译器
  • 模板编译器的作用就是将 template 选项编译成 render 函数,render 函数是渲染的关键
  • 鉴于以上两点,使用运行时构建时,不能出现 template 选项
    • 因为此时没有模板编译器,但是有一种情况除外,即 webpack + vue-loader 情况下单文件组件中出现 template 是可以的
  • 使用 Vue-Cli 生成项目时,会提醒使用哪种构建方式,npm 包默认导出的是运行时构建
    • 如果需要使用独立构建,需要在 Webpack 中配置 alias

目前暂时发现的两者的应用场景上的区别有

  • 需要注意 Vue 实例化时的方式,运行时构建方式下实例化 Vue 时,不要出现 template 属性
  • index.html 中不要出现 template 或者是通过 vue-router 渲染的 route-view
  • 以上区别讨论的场景均为使用 webpack + vue-loader 单文件组件下

解决办法也很简单,只需在 Webpack 配置中设置一下 Vue 的别名即可

1
2
3
4
5
resolve: {
alias: {
'vue': 'vue/dist/vue.js'
}
}

h => h(App)

vuejs 中,可以把 h 函数仅是作为 createElement 函数的缩写,所以上述代码可以理解为

1
2
3
render: function (createElement){
return createElement(app)
}

render 方法

1
2
3
4
5
6
7
8
render: function (createElement) {
return createElement(
// tag name 标签名称
'h' + this.level,
// 子组件中的阵列
this.$slots.default
)
}

也是 2.0 新增的函数,可以直接给绑定节点渲染一个 Vue 组件,如果在 1.x 的版本下,就应该使用

1
2
3
4
5
6
7
8
9
new Vue({
el: '#app',
components: { App }
});

// html
<div id='app'>
<app></app>
</div>

.babelrc

.bowerrc 是一样的原理,都是用来配置文件,只需要在根目录新建一个名为 .babelrc 文件,然后添加

1
2
3
4
{
'presets': ['es2015', 'stage-0'],
'plugins': ['transform-runtime']
}

就可以达到和上面一样的效果

url-loader

url-loader 是对 file-loader 的上层封装,比如可以在 Webpack 中对图片的加载器进行配置

1
2
3
4
{
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192'
}

这样一来,在小于 8K 的图片将直接以 base64 的形式内联在代码中,可以减少一次 HTTP 请求,所以在处理一般小图片的时候就可以使用 url-loader 来转为 base64,其他情况照常使用 file-loader

webpack 中的 loader 和 plugin

Webpack 是一个模块打包器(module bundler),提供了一个核心,核心提供了很多开箱即用的功能,同时它可以用 loaderplugin 来扩展,Webpack 常用配置包括 devtoolentryoutputmoduleresolvepluginsexternals 等,这里我们主要介绍 Webpack 常用的 loaderplugin

Webpack 允许我们使用 loader 来处理文件,loader 是一个导出为 functionNode.js 模块,可以将匹配到的文件进行一次转换,同时 loader 可以链式传递

使用方式

一般 loader 的使用方式分为三种,第一种,在 webpack.config.js 中配置(较多)

1
2
3
4
5
6
7
8
9
10
module.exports = {
module: {
rules: [
{
test: /\.txt$/,
use: 'raw-loader'
}
]
}
}

第二种,通过命令行参数方式

1
webpack --module-bind 'txt=raw-loader'

第三种,通过内联使用

1
import txt from 'raw-loader!./file.txt';

一些比较常用的 loader

  • 样式有 style-loadercss-loaderless-loadersass-loader
  • 文件有 raw-loaderfile-loaderurl-loader
  • 编译有 babel-loadercoffee-loaderts-loader
  • 校验测试有 mocha-loaderjshint-loadereslint-loader

比如下面配置,可以匹配 .scss 的文件,分别经过 sass-loadercss-loaderstyle-loader 的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: { sourceMap: true, modules: true } },
{ loader: 'sass-loader', options: { sourceMap: true } }
],
exclude: /node_modules/
}
]
}
}
  • style-loader,将创建一个 style 标签将 CSS 文件嵌入到 HTML
  • css-loader,处理其中的 @importurl()
  • sass-loader,转化 sassCSS 文件,并且包一层 module.exports 成为一个 js module

其他一些相关 loader 介绍

  • vue-loadercoffee-loaderbabel-loader
    • 等可以将特定文件格式转成 JavaScript 模块
    • 将其他语言转化为 JavaScript 语言和编译下一代 JavaScript 语言(ES6
  • file-loaderurl-loader
    • 可以处理(静态)资源
  • file-loader
    • 可以复制和放置资源位置,并可以指定文件名模板,用 Hash 命名更好利用缓存
  • url-loader
    • 可以将小于配置 limit 大小的文件转换成内敛 data url 的方式,减少请求
  • raw-loader
    • 可以将文件以字符串的形式返回
  • imports-loaderexports-loader
    • 等可以向模块注入变量或者提供导出模块功能,常见场景是
      • jQuery 插件注入 $imports-loader?$=jQuery
      • 禁用 AMDimports-loader?define=false
      • 等同于 var $ = require('jQuery')var define = false;
  • expose-loader
    • 暴露对象为全局变量
# Vue

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×