从0到1搭建webpack2+vue2自定义模板详细教程

前言

webpack2和vue2已经不是新鲜东西了,满大街的文章在讲解webpack和vue,可是不少内容写的不是很详细,对于不少个性化配置仍是须要本身过一遍文档。Vue官方提供了多个vue-templates,基于vue-cli用官方的webpack模板居多,不过对于不少人来讲,官方的webpack模板的配置仍是过于复杂,对于咱们了解细节实现不是很好,因此想本身从零开始搭建一个模板工程,也顺便从新认识一下webpack和vue工程化的细节。javascript

webpack 核心概念

Webpack 是当下最热门的前端资源模块化管理和打包工具。它能够将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还能够将按需加载的模块进行代码分隔,等到实际须要的时候再异步加载。经过 loader 的转换,任何形式的资源均可以视做模块,好比 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。css

官方网站:https://webpack.js.org/html

安装

在开始前,先要确认你已经安装Node.js的最新版本。使用 Node.js 最新的 LTS 版本,是理想的起步。使用旧版本,你可能遇到各类问题,由于它们可能缺乏 webpack 功能或缺乏相关 package 包。前端

本地局部安装:vue

# 安装 latest release
npm install --save-dev webpack
# 简写模式
npm install -D webpack
# 安装特定版本
npm install --save-dev webpack@<version>

全局安装:html5

npm install -g webpack

注意:不推荐全局安装 webpack。这会锁定 webpack 到指定版本,而且在使用不一样的 webpack 版本的项目中可能会致使构建失败。可是全局安装能够在命令行调用 webpack 命令。java

【补充】npm install 安装模块参数说明:node

-g, --global 全局安装(global)
-S, --save 安装包信息将加入到dependencies(生产阶段的依赖)
-D, --save-dev 安装包信息将加入到devDependencies(开发阶段的依赖),因此开发阶段通常使用它
-O, --save-optional 安装包信息将加入到optionalDependencies(可选阶段的依赖)
-E, --save-exact 精确安装指定模块版本

npm 相关的更多命令参考这篇文章:npm 经常使用命令详解webpack

而后在根目录下建立一个 webpack.config.js 文件后,你能够经过配置定义webpack的相关操做。git

入口(Entry)

入口起点告诉 webpack 从哪里开始,并遵循着依赖关系图表知道要打包什么。能够将您应用程序的入口起点认为是根上下文(contextual root)或 app 第一个启动文件。

单个入口(简写)语法:
用法:entry: string|Array<string>

webpack.config.js:

module.exports = {
  entry: './src/main.js'
};

对象语法:
用法:entry: {[entryChunkName: string]: string|Array<string>}

webpack.config.js:

module.exports = {
  entry: {
    app: './src/main.js',
    vendor: ['vue']
  }
};

这里咱们将vue做为库vendor打包,业务逻辑代码做为app打包,实现了多个入口,同时也能够将多个页面分开打包。

多页面应用程序一般使用对象语法构建。对象语法是“可扩展的 webpack 配置”,可重用而且能够与其余配置组合使用。这是一种流行的技术,用于将关注点(concern)从环境(environment)、构建目标(build target)、运行时(runtime)中分离。而后使用专门的工具(如webpack-merge)将它们合并。

注:vue-cli 生成的模板中build文件夹下有四个配置文件:

后三个文件经过webpack-merge插件合并了基本配置,将不一样环境下的配置拆分多个文件,这样更加方便管理。

出口(Output)

将全部的资源(assets)归拢在一块儿后,还须要告诉 webpack 在哪里打包应用程序。webpack 的 output 属性描述了如何处理归拢在一块儿的代码(bundled code)。output 选项控制 webpack 如何向硬盘写入编译文件。注意,即便能够存在多个入口起点,但只指定一个输出配置。

在 webpack 中配置output 属性的最低要求是,将它的值设置为一个对象,包括如下两点:

  • output.filename:编译文件的文件名;

  • output.path对应一个绝对路径,此路径是你但愿一次性打包的目录。

单个入口:

const path = require('path');
module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'build')  //__dirname + '/build'
  }
}

多个入口:
若是你的配置建立了多个 "chunk"(例如使用多个入口起点或使用相似CommonsChunkPlugin 的插件),你应该使用如下的替换方式来确保每一个文件名都不重复。

  • [name] 被 chunk 的 name 替换。

  • [hash] 被 compilation 生命周期的 hash 替换。

  • [chunkhash] 被 chunk 的 hash 替换。

const path = require('path');
module.exports = {
  entry: {
    app: './src/main.js',
    vendor: ['vue']
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'build')
  }
}

// 写入到硬盘:./build/app.js, ./build/vendor.js

加载器(Loaders)

loader 用于对模块的源代码进行转换。loader 可使你在 require() 或"加载"模块时预处理文件。所以,loader 相似于其余构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 能够将文件从不一样的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至容许你在 JavaScript 中 require() CSS文件!

在你的应用程序中,有三种方式使用 loader:

这里咱们主要说明一下使用webpack.config.js配置,使用loader须要在module的rules下配置相应的规则,以css-loader的webpack.config.js为例说明:

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

这三种配置方式等效:

{test: /\.css$/, use: 'css-loader'}
{test: /\.css$/, loader: 'css-loader',options: { modules: true }}
{test: /\.css$/, use: {
    loader: 'css-loader',
    options: {
      modules: true
    }
}}

注:loader/query能够和options能够在同一级使用,可是不要使用use和options在同一级使用。

CSS样式分离

为了用 webpack 对 CSS 文件进行打包,你能够像其它模块同样将 CSS 引入到你的 JavaScript 代码中,同时用css-loader(像 JS 模块同样输出 CSS),也能够选择使用ExtractTextWebpackPlugin(将打好包的 CSS 提出出来并输出成 CSS 文件)。

引入 CSS:

import 'bootstrap/dist/css/bootstrap.css';

安装css-loader和style-loader:

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

在 webpack.config.js 中配置以下:

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

资源路径处理

由于.png等图片文件不是一个 JavaScript 文件,你须要配置 Webpack 使用file-loader或者url-loader去处理它们。使用它们的好处:

  • file-loader 能够指定要复制和放置资源文件的位置,以及如何使用版本哈希命名以得到更好的缓存。此外,这意味着 你能够就近管理你的图片文件,可使用相对路径而不用担忧布署时URL问题。使用正确的配置,Webpack 将会在打包输出中自动重写文件路径为正确的URL。

  • url-loader 容许你有条件将文件转换为内联的 base-64 URL(当文件小于给定的阈值),这会减小小文件的 HTTP 请求。若是文件大于该阈值,会自动的交给 file-loader 处理。

安装 file-loader 和 url-loader:

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

配置说明:

{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'url-loader',
    options: {
        limit: 10000,
        name: 'img/[name]_[hash:7].[ext]'
    }
},
{
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
    loader: 'url-loader',
    options: {
        limit: 10000,
        name: 'fonts/[name].[hash:7].[ext]'
    }
}

插件(Plugins)

因为 loader 仅在每一个文件的基础上执行转换,而插件(plugins)最经常使用于(但不限于)在打包模块的“compilation”和“chunk”生命周期执行操做和自定义功能(查看更多)。webpack 的插件系统极其强大和可定制化

想要使用一个插件,你只须要 require() 它,而后把它添加到 plugins 数组中。多数插件能够经过选项(option)自定义。你也能够在一个配置文件中由于不一样目的而屡次使用同一个插件,你须要使用 new 建立实例来调用它。

生产环境构建

对于Vue生产环境构建过程当中压缩应用代码和使用Vue.js 指南 - 删除警告去除 Vue.js 中的警告,这里咱们参考vue-loader文档中的配置说明:

if (process.env.NODE_ENV === 'production') {
    // http://vue-loader.vuejs.org/zh-cn/workflow/production.html
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"production"'
            }
        }),
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: false,
            compress: {
                warnings: false
            }
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true
        })
    ])
}

显然咱们不想在开发过程当中使用这些配置,因此这里咱们须要使用环境变量动态构建,咱们也可使用两个分开的 Webpack 配置文件,一个用于开发环境,一个用于生产环境,相似于vue-cli中使用 webpack-merge 合并配置的方式。

可使用 Node.js 模块的标准方式:在运行 webpack 时设置环境变量,而且使用 Node.js 的process.env来引用变量。NODE_ENV变量一般被视为事实标准(查看这里)。使用cross-env包来跨平台设置(cross-platform-set)环境变量。

安装cross-env:

npm install --save-dev cross-env

设置package.json中的scripts字段:

"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
}

这里咱们使用了cross-env插件,cross-env使得你可使用单个命令,而无需担忧为平台正确设置或使用环境变量。

模块热替换

模块热替换功能会在应用程序运行过程当中替换、添加或删除模块,而无需从新加载页面。这使得你能够在独立模块变动后,无需刷新整个页面,就能够更新这些模块,极大地加速了开发时间。

这里咱们使用webpack-dev-server插件,webpack-dev-server 为你提供了一个服务器和实时重载(live reloading)功能。webpack-dev-server是一个小型的node.js Express服务器,它使用webpack-dev-middleware中间件来为经过webpack打包生成的资源文件提供Web服务。它还有一个经过Socket.IO链接着webpack-dev-server服务器的小型运行时程序。webpack-dev-server发送关于编译状态的消息到客户端,客户端根据消息做出响应。

安装 webpack-dev-server:

npm install --save-dev webpack-dev-server

安装完成以后,你应该可使用 webpack-dev-server 了,方式以下:

webpack-dev-server --open

上述命令应该自动在浏览器中打开 http://localhost:8080

webpack.config.js配置:

module.exports = {
    ...
    devServer: {
        historyApiFallback: true, // 任意的 404 响应都替代为 index.html
        hot: true, // 启用 webpack 的模块热替换特性
        inline: true // 启用内联模式
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
    ...
}

更多的配置说明能够看文档:DevServer

动态生成 html 文件

该插件将为你生成一个HTML5文件,其中包括使用script标签的body中的全部webpack包,也就是咱们不须要手动经过script去引入打包生成的js,特别是若是咱们生成的文件名是动态变化的,使用这个插件就能够轻松的解决,只需添加插件到您的webpack配置以下:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    ...
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'index.html',
            inject: true
        })
    ]
    ...
}

提取 CSS 文件

extract-text-webpack-plugin是一个 能够将 *.vue 文件内的 <style> 提取,以及JavaScript 中导入的 CSS 提取为单个 CSS 文件。配置文档具体见这里:extract-text-webpack-plugin

安装:

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

配置:

const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: "css-loader"
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("styles.css"),
  ]
}

同时支持咱们能够配置生成多个css文件,这样咱们能够将业务逻辑代码和引用的样式组件库分离。

const ExtractTextPlugin = require('extract-text-webpack-plugin');

// Create multiple instances
const extractCSS = new ExtractTextPlugin('stylesheets/[name]-one.css');
const extractLESS = new ExtractTextPlugin('stylesheets/[name]-two.css');

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: extractCSS.extract([ 'css-loader', 'postcss-loader' ])
      },
      {
        test: /\.less$/i,
        use: extractLESS.extract([ 'css-loader', 'less-loader' ])
      },
    ]
  },
  plugins: [
    extractCSS,
    extractLESS
  ]
};

clean-webpack-plugin

在编译前,删除以前编译结果目录或文件:

npm install --save-dev clean-webpack-plugin

配置:

plugins: [
    new CleanWebpackPlugin(['dist'])
]

这样当咱们在构建的时候能够自动删除以前编译的代码。

解析(Resolve)

这些选项能设置模块如何被解析。webpack 提供合理的默认值,可是仍是可能会修改一些解析的细节。

resolve: {
  alias: {
    'vue$': 'vue/dist/vue.esm.js',
    '@': path.join(__dirname, 'src')
  },
  extensions: ['.js', '.json', '.vue', '.css']
}

咱们使用最多的就是别名(alias)和自动解析肯定的扩展(extensions),例如上面的@能够代替项目中src的路径,例如:

import tab from '@/components/tab.vue'

咱们引用src/components目录下的tab.vue组件,不须要经过../之类的计算文件相对路径。这里的extensions可让咱们在引入模块时不带扩展:

import tab from '@/components/tab'

至此咱们已经学习了咱们项目devDependencies依赖中经常使用的模块:

webpack 
css-loader / style-loader
file-loader / url-loader 
cross-env 
webpack-dev-server 
html-webpack-plugin 
extract-text-webpack-plugin
clean-webpack-plugin

这里咱们只说明了css、图片、html模板资源webpack相关的加载器和插件,对于js相关的内容丝毫没有提到,显然这是不合乎情理的。之因此要把js单独拿出来是由于js相关的内容很重要,独立出来详细去概括一下更合适。


webpack 中如何使用 es6 ~ es8?

做为一个前端,相信 es6 几乎是无人不知,不少人也必定知道可使用Babel作语法转换,可是对于Babel有哪一些版本,每一个版本支持的es6语法有哪一些应该不是全部人都清楚的,这就是这部份内容要写的意义。毕竟若是咱们的插件只用到了es6中的没一些新特性,为此将整个包引入就有点不太合适,另外为了更好的用上新特性,咱们至少要明白有哪一些新特性吧。

ECMAScript 标准创建的过程

ECMAScript 和 JavaScript 的关系在此再也不赘述,建议阅读一下阮一峰老师的《ECMAScript 6简介》,咱们须要了解的是从ECMAScript 2016开始,ECMAScript将进入每一年发布一次新标准的阶段。制定ECMAScript 标准的组织是ECMAScript TC39TC39(ECMA技术委员为39)是推进JavaScript发展的委员会。 它的成员是都是企业(主要是浏览器厂商)。TC39会按期的开会, 会议的主要成员时是成员公司的表明,以及受邀请的专家。

一种新的语法从提案到变成正式标准,须要经历五个阶段。每一个阶段的变更都须要由 TC39 委员会批准。

  • Stage 0 - Strawman(展现阶段)

  • Stage 1 - Proposal(征求意见阶段)

  • Stage 2 - Draft(草案阶段)

  • Stage 3 - Candidate(候选人阶段)

  • Stage 4 - Finished(定案阶段)

建议看一下alinode 团队的图说ECMAScript新标准(一)就能够大体了解整个过程。

安装 Babel

Babel 如今的官网提供了一个能够根据你的工具提示下载合适的包,具体见这里:Using Babel

若是你想要在命令行使用Babel,你能够安装babel-cli,可是全局的安装babel-cli不是一个好的选择,由于这样限定了你Babel的版本;若是你须要在一个Node项目中使用Babel,你可使用babel-core。

咱们这里天然选择webpack构建咱们的工程,下载方案以下:

npm install --save-dev babel-loader babel-core

而后咱们须要在项目根目录下创建.babelrc文件:

{
  "presets": [],
  "plugins": []
}

注:在window下没法经过 右键=>新建 命令来建立以点开头的文件和文件夹,咱们能够经过下面的命令生成.babelrc文件:

type NUL > .babelrc

Linux和Mac下能够经过touch命令生成:

touch .babelrc

Babel 预设(presets)

Babel是一个编译器。 在高层次上,它有3个阶段,它运行代码:解析,转换和生成(像许多其余编译器)。默认状况下,Babel 6并无携带任何转换器,所以若是对你的代码使用Babel的话,它将会原文输出你的代码,不会有任何的改变。所以你须要根据你须要完成的任务来单独安装相应的插件。

你能够经过安装插件(plugins)或预设(presets,也就是一组插件)来指示 Babel 去作什么事情。Babel 提供了多个版本的官方预设:

babel-preset-env

babel-preset-env能够根据你配置的选项,自动添加一些其余的转换器,来知足你当前的装换需求。.babelrc文件新增了options选项:

{
  "presets": ["env", options]
}

具体的配置内容:

  • targets.node 支持到哪一个版本的 node

  • targets.browsers 支持到哪一个版本的浏览器

  • loose 启动宽松模式,配合 webpack 的 loader 使用

  • modules 使用何种模块加载机制

  • debug 开启调试模式

  • include 包含哪些文件

  • exclude 排除哪些文件

  • useBuiltIns 是否对 babel-polyfill 进行分解,只引入所需的部分

babel-preset-es2015

es2015(ES6)相关方法转译使用的插件,具体见文档

  • check-es2015-constants // 检验const常量是否被从新赋值

  • transform-es2015-arrow-functions // 编译箭头函数

  • transform-es2015-block-scoped-functions // 函数声明在做用域内

  • transform-es2015-block-scoping // 编译const和let

  • transform-es2015-classes // 编译class

  • transform-es2015-computed-properties // 编译计算对象属性

  • transform-es2015-destructuring // 编译解构赋值

  • transform-es2015-duplicate-keys // 编译对象中重复的key,实际上是转换成计算对象属性

  • transform-es2015-for-of // 编译for...of

  • transform-es2015-function-name // 将function.name语义应用于全部的function

  • transform-es2015-literals // 编译整数(8进制/16进制)和unicode

  • transform-es2015-modules-commonjs // 将modules编译成commonjs

  • transform-es2015-object-super // 编译super

  • transform-es2015-parameters // 编译参数,包括默认参数,不定参数和解构参数

  • transform-es2015-shorthand-properties // 编译属性缩写

  • transform-es2015-spread // 编译展开运算符

  • transform-es2015-sticky-regex // 正则添加sticky属性

  • transform-es2015-template-literals // 编译模版字符串

  • transform-es2015-typeof-symbol // 编译Symbol类型

  • transform-es2015-unicode-regex // 正则添加unicode模式

  • transform-regenerator // 编译generator函数

babel-preset-es2016

es2016(ES7)相关方法转译使用的插件,具体见文档

  • transform-exponentiation-operator // 编译幂运算符

babel-preset-es2017

es2017(ES8)相关方法转译使用的插件,具体见文档

  • syntax-trailing-function-commas // function最后一个参数容许使用逗号

  • transform-async-to-generator // 把async函数转化成generator函数

babel-preset-latest

latest是一个特殊的presets,包括了es2015,es2016,es2017的插件,不过已经废弃,使用babel-preset-env代替,具体见文档

stage-x(stage-0/1/2/3/4)

stage-x预设中的任何转换都是还没有被批准为发布Javascript的语言(如ES6 / ES2015)的更改。

stage-x和上面的es2015等有些相似,可是它是按照JavaScript的提案阶段区分的,一共有5个阶段。而数字越小,阶段越靠后,存在依赖关系。也就是说stage-0是包括stage-1的,以此类推。

babel-preset-stage-4:

stage-4的插件:

babel-preset-stage-3:

除了stage-4的内容,还包括如下插件:

babel-preset-stage-2:

除了stage-3的内容,还包括如下插件:

babel-preset-stage-1:

除了stage-2的内容,还包括如下插件:

babel-preset-stage-0:

除了stage-1的内容,还包括如下插件:

为了方便,咱们暂时引用 babel-preset-envbabel-preset-stage-2这两个预设。为了启用预设,必须在.babelrc文件中定义预设的相关配置,这里参考vue-cli 模板中的配置
安装:

npminstall --save-dev babel-preset-env babel-preset-stage-2

.babelrc配置说明:

{
  "presets": [
    ["env", { 
      "modules": false 
    }],
    "stage-2"
  ]
}

Babel 插件(plugins)

咱们看一下预设的构成就知道,其实就是plugins的组合。若是你不采用presets,彻底能够单独引入某个功能,好比如下的设置就会引入编译箭头函数的功能,在.babelrc文件中进行配置:

{
  "plugins": ["transform-es2015-arrow-functions"]
}

babel-polyfill 与 babel-runtime

Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,好比Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(好比 Object.assign)都不会转码。

举例来讲,ES6在 Array 对象上新增了 Array.from 方法。Babel就不会转码这个方法。若是想让这个方法运行,必须使用 babel-polyfill ,为当前环境提供一个垫片。babel-polyfill 是对浏览器缺失API的支持。

babel-runtime 是为了减小重复代码而生的。 babel生成的代码,可能会用到一些_extend(), classCallCheck() 之类的工具函数,默认状况下,这些工具函数的代码会包含在编译后的文件中。若是存在多个文件,那每一个文件都有可能含有一份重复的代码。babel-runtime插件可以将这些工具函数的代码转换成require语句,指向为对babel-runtime的引用,如require('babel-runtime/helpers/classCallCheck'). 这样, classCallCheck的代码就不须要在每一个文件中都存在了。

启用插件 babel-plugin-transform-runtime 后,Babel 就会使用 babel-runtime 下的工具函数。除此以外,babel 还为源代码的非实例方法(Object.assign,实例方法是相似这样的 "foobar".includes("foo"))和 babel-runtime/helps 下的工具函数自动引用了 polyfill。这样能够避免污染全局命名空间,很是适合于 JavaScript 库和工具包的实现。

总结:

  • 具体项目仍是须要使用 babel-polyfill,只使用 babel-runtime 的话,实例方法不能正常工做(例如 "foobar".includes("foo"));

  • JavaScript 库和工具可使用 babel-runtime,在实际项目中使用这些库和工具,须要该项目自己提供 polyfill。

  • transform-runtime只会对es6的语法进行转换,而不会对新api进行转换。若是须要转换新api,就要引入babel-polyfill。

安装插件

npm install --save-dev babel-plugin-transform-runtime

.babelrc 配置:

{
  "plugins": ["transform-runtime", options]
}

options主要有如下设置项:

  • helpers: boolean,默认true,使用babel的helper函数;

  • polyfill: boolean,默认true,使用babel的polyfill,可是不能彻底取代babel-polyfill;

  • regenerator: boolean,默认true,使用babel的regenerator;

  • moduleName: string,默认babel-runtime,使用对应module处理。

注:默认moduleName为babel-runtime,这里咱们能够没必要显式的下载babel-runtime,由于babel-plugin-transform-runtime依赖于babel-runtime。

babel-register

babel-register 模块改写 require 命令,为它加上一个钩子。此后,每当使用 require 加载 .js 、 .jsx 、 .es 和 .es6 后缀名的文件,就会先用Babel进行转码。引入babel-register,这样后面的文件就能够用 import 代替require,import的优势在于能够引入所需方法或者变量,而不须要加载整个模块,提升了性能。

安装:

npm install --save-dev babel-register

这部分咱们又介绍了下面几个模块的安装:

babel-loader
babel-core
babel-preset-env 
babel-preset-stage-2 
babel-plugin-transform-runtime
babel-register

webpack 中如何使用 vue?

既然本文的目标是vue的自定义模板工程,那么天然这里须要单独介绍一下webpack中vue相关的插件。

Vue2文件比较

npm 安装:

npm install --save vue

vue2 通过 2.2 版本升级后, 文件变成 8 个:

UMD CommonJS ES Module
独立构建 vue.js vue.common.js vue.esm.js
运行构建 vue.runtime.js vue.runtime.common.js vue.runtime.esm.js

vue.min.js 和 vue.runtime.min.js 都是对应的压缩版。

  • AMD:异步模块规范

  1. 没有单独提供 AMD 模块的版本,可是UMD版本中进行了包装,能够直接用做 AMD 模块,使用方法以下:

define(["Vue"],function(Vue) {
    function myFn() {
        ...
    }
    return myFn;
});
  • CommonJS:
    node中经常使用的模块规范,经过require引入模块,module.exports导出模块。

...
function Vue$3() {
   ...
}
...
module.exports = Vue$3;
  • UMD: 通用模块规范
    兼容了AMD和CommonJS,同时还支持老式的“全局”变量规范:

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global.Vue = factory());
}(this, (function () { 'use strict';
    ...
    function Vue$3() {
        ...
    }
    ...
    return Vue$3;
})));
  • ES Module
    ES6在语言标准的层面上,实现的模块功能。模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其余模块提供的功能。

...
function Vue$3() {
   ...
}
export default Vue$3;

总结:

  • vue.js 和 vue.runtime.js 能够用于直接 CDN 引用;

  • vue.common.js和vue.runtime.common.js可使用Webpack1 / Browserify 打包构建;

  • vue.esm.js和vue.runtime.esm.js可使用Webpack2 / rollup 打包构建。

vue有两种构建方式,独立构建和运行时构建。它们的区别独立构建前者包含模板编译器而运行构建不包含。模板编译器的职责是将模板字符串编译为纯 JavaScript 的渲染函数。若是你想要在组件中使用 template 选项,你就须要编译器。

  • 独立构建包含模板编译器并支持 template 选项。 它也依赖于浏览器的接口的存在,因此你不能使用它来为服务器端渲染。

  • 运行时构建不包含模板编译器,所以不支持 template 选项,只能用 render 选项,但即便使用运行时构建,在单文件组件中也依然能够写模板,由于单文件组件的模板会在构建时预编译为 render 函数。运行时构建比独立构建要轻量30%,只有 17.14 Kb min+gzip大小。

独立构建方式能够这样使用template选项:

import Vue from 'vue'
new Vue({
  template: `
    <div id="app">
      <h1>Basic</h1>
    </div>
  `
}).$mount('#app')

这里咱们使用ES Module规范,默认 NPM 包导出的是运行时构建。为了使用独立构建,在 webpack 配置中添加下面的别名:

resolve: {
  alias: {
    'vue$': 'vue/dist/vue.esm.js'
  }
}

vue-loader

安装:

npm install --save-dev vue-loader vue-template-compiler

vue-loader 依赖于 vue-template-compiler。

vue-loader 是一个 Webpack 的 loader,能够将用下面这个格式编写的 Vue 组件转换为 JavaScript 模块。这里有一些 vue-loader 提供的很酷的特性:

  • ES2015 默认支持;

  • 容许对 Vue 组件的组成部分使用其它 Webpack loaders,好比对 <style> 使用 SASS 和对 <template> 使用 Jade;

  • .vue 文件中容许自定义节点,而后使用自定义的 loader 处理他们;

  • <style> <template> 中的静态资源看成模块来对待,并使用 Webpack loaders 进行处理;

  • 对每一个组件模拟出 CSS 做用域;

  • 支持开发期组件的热重载。

简而言之,编写 Vue.js 应用程序时,组合使用 Webpack 和 vue-loader 能带来一个现代,灵活而且很是强大的前端工做流程。

在 Webpack 中,全部的预处理器须要匹配对应的 loader。 vue-loader 容许你使用其它 Webpack loaders 处理 Vue 组件的某一部分。它会根据 lang 属性自动推断出要使用的 loaders。

上述咱们提到extract-text-webpack-plugin插件提取css,这里说明一下.vue中style标签之间的样式提取的办法:

var ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            css: ExtractTextPlugin.extract({
              use: 'css-loader',
              fallback: 'vue-style-loader' // <- 这是vue-loader的依赖,因此若是使用npm3,则不须要显式安装
            })
          }
        }
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("app.css")
  ]
}

pug 模板

用过模板的都知道,熟悉了模板写起来快多了,大名鼎鼎的jade恐怕无人不知吧。pug是什么鬼?第一次听到的时候我也好奇了,而后查了一下才知道,Pug原名不叫Pug,原来是大名鼎鼎的jade,后来因为商标的缘由,改成Pug,哈巴狗。如下是官方解释:

it has been revealed to us that "Jade" is a registered trademark, and as a result a rename is needed. After some discussion among the maintainers, "Pug" has been chosen as the new name for this project.

简单看了看仍是原来jade熟悉的语法规则,果断在这个模板工程里面用上。

vue-loader里面对于模版的处理方式略有不一样,由于大多数 Webpack 模版处理器(好比 pug-loader)会返回模版处理函数,而不是编译的 HTML 字符串,咱们使用原始的 pug 替代 pug-loader:

npm install pug --save-dev

使用:

<template lang="pug">
div
  h1 Hello world!
</template>

重要: 若是你使用 vue-loader@<8.2.0, 你还须要安装 template-html-loader

PostCSS

安装vue-loader的时候默认安装了postcss,由vue-loader处理的 CSS 输出,都是经过PostCSS进行做用域重写,你还能够为 PostCSS 添加自定义插件,例如autoprefixer或者CSSNext

在 webpack 工程中使用 postcss,咱们须要下载 postcss-loader:

npm install --save-dev postcss-loader

cssnext

cssnext 是一个 CSS transpiler,容许你使用最新的 CSS 语法。cssnext 把 新 CSS 规范转换成兼容性更强的 CSS,因此不须要等待各类浏览器支持。

安装:

npm install --save-dev postcss-cssnext

postcss.config.js:

module.exports = {
    plugins: [
        require('postcss-cssnext')
    ]
}

webpack.config.js:

module.exports = {
    module: {
        loaders: [
            {
                test:   /\.css$/,
                use: ['style-loader', 'css-loader', 'postcss-loader']
            }
        ]
    }
}

cssnext 依赖了autoprefixer,因此咱们无需显式下载autoprefixer。更多关于postcss的插件能够看这里:postcss plugins

这一部分咱们学习了这些依赖:

vue
vue-loader 
vue-template-compiler
pug
postcss-loader
postcss-cssnext

webpack2 开启 eslint 校验

规范本身的代码从ESlint开始。ESlint和webpack集成,在babel编译代码开始前,进行代码规范检测。这里咱们使用javascript-style-standard风格的校验。

主要依赖的几个包:

eslint —— 基础包
eslint-loader —— webpack loader
babel-eslint —— 校验babel
eslint-plugin-html —— 提取并检验你的 .vue 文件中的 JavaScript
eslint-friendly-formatter —— 生成美化的报告格式

# javascript-style-standard 依赖的包
eslint-config-standard
eslint-plugin-import
eslint-plugin-node
eslint-plugin-promise
eslint-plugin-standard

安装:

npm install --save-dev eslint eslint-loader babel-eslint eslint-plugin-html eslint-friendly-formatter eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-node eslint-plugin-promise eslint-plugin-standard

关于eslint的配置方式,比较多元化,具体能够看配置文档

  • js注释

  • .eslintrc.*文件

  • package.json里面配置eslintConfig字段

安装eslint-loader以后,咱们能够在webpack配置中使用eslint加载器。webpack.config.js

...
module: {
  loaders: [
    {
         test: /\.vue|js$/,
         enforce: 'pre',
         include: path.resolve(__dirname, 'src'),
         exclude: /node_modules/,
         use: [{
             loader: 'eslint-loader',
             options: {
                 formatter: require('eslint-friendly-formatter')
             }
         }]
    }
  ]
},
...

此外,咱们既能够在webpack配置文件中指定检测规则,也能够遵循最佳实践在一个专门的文件中指定检测规则,咱们就采用后面的方式。
在根目录下:

touch .eslintrc.js

.eslintrc.js:

module.exports = {
  root: true,
  parser: 'babel-eslint',
  parserOptions: {
    sourceType: 'module'
  },
  env: {
    browser: true
  },
  extends: 'standard',
  // required to lint *.vue files
  plugins: [
    'html'
  ],
  // add your custom rules here
  rules: {
    // allow paren-less arrow functions
    'arrow-parens': 0,
    // allow async-await
    'generator-star-spacing': 0,
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
  }
}

这部份咱们主要学习了一下eslint相关插件的含义和配置方法。

建立属于你的模板

若是你对官方的模板不感兴趣,你能够本身fork下来而后进行修改(或者从新写一个),而后用 vue-cli 来调用。由于 vue-cli 能够直接拉取 git源:

vue init username/repo my-project

这里咱们参考vue-cli的模板工程本身写一个模板工程,主要是须要经过meta.*(js,json)进行配置:

module.exports = {
  "helpers": {
    "if_or": function (v1, v2, options) {
      if (v1 || v2) {
        return options.fn(this);
      }

      return options.inverse(this);
    }
  },
  "prompts": {
    "name": {
      "type": "string",
      "required": true,
      "message": "Project name"
    },
    "version": {
      "type": "string",
      "required": false,
      "message": "Project version",
      "default": "1.0.0"
    },
    "description": {
      "type": "string",
      "required": false,
      "message": "Project description",
      "default": "A Vue.js project"
    },
    "author": {
      "type": "string",
      "message": "Author"
    },
    "router": {
      "type": "confirm",
      "message": "Install vue-router?"
    },
    "vuex": {
      "type": "confirm",
      "message": "Install vuex?"
    }
  },
  "completeMessage": "To get started:\n\n  {{^inPlace}}cd {{destDirName}}\n  {{/inPlace}}npm install\n  npm run dev\n\nDocumentation can be found at https://github.com/zhaomenghuan/vue-webpack-template"
};

这里咱们就是采用最简单的方式,对于vue-router、vuex的配置每一个人习惯不同,因此不写在模板工程里面。

而后使用vue-cli使用这个模板建立工程,没有安装vue-cli的执行:

npm install --global vue-cli

而后建立工程:

# 建立一个基于 webpack 模板的新项目
vue init zhaomenghuan/vue-webpack-template my-project
# 安装依赖,走你
cd my-project
npm install
npm run dev

这里按照国际惯例安利一下本文的模板工程:vue-webpack-template

参考

webpack官方文档
babel官方文档
vue-loader中文文档
JavaScript books by Dr. Axel Rauschmayer
ES7新特性及ECMAScript标准的制定流程
如何写好.babelrc?Babel的presets和plugins配置解析
babel的polyfill和runtime的区别
webpack2集成eslint

clipboard.png


近期在segmentfault讲堂开设了一场关于html5+ App开发工程化实践之路的讲座,欢迎前来围观:https://segmentfault.com/l/15...