前言

为什么要使用模块化进行开发?

  • 避免全局变量命名污染
  • 代码复用
  • 将项目功能分块开发,易于团队合作,便于后期维护修改

在 ES6 之前,我们要想进行模块化开发,就必须借助于其他的工具。

webpack 可以让我们进模块化开发,并且会帮助我们处理模块间的各种依赖,并且将其进行整合打包。
在打包的过程中,还可以对资源进行处理,比如压缩图片,将 scss 转换成 css,将 ES6 语法转换成 ES5 语法,将 TypeScript 转成 JavaScript 等等操作。

安装

webpack 依赖于 Node.js,所以需要先安装 Node.js

安装 webpack

npm install webpack@3.6.0 —save-dev

全局安装 webpack

npm install webpack@3.6.0 -g

webpack 安装 3.6.0 版本,因为vue cli2依赖该版本,vue cli2 能看到 webpack 的配置文件,而 vue cli3 看不到

使用 webpack 命令进行打包

webpack [打包路径] [输出路径]

配置

可以配置 webpack.config.js 文件来实现自动打包,详细可以看官方文档

  1. // webpack.config.js
  2. // 动态获取路径需要用来的模块
  3. const path = require('path');
  4. module.exports = {
  5. // 入口
  6. entry: './src/main.js',
  7. // 出口
  8. output: {
  9. // 出口路径
  10. // 动态获取绝对路径
  11. path: path.resolve(__dirname, 'dist'),
  12. // 打包生成文件的名称
  13. filename: 'bundle.js'
  14. },
  15. }

通常我们使用 package.json 中的脚本来进行打包

将 build 映射到 webpakc 命令

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "build": "webpack"
}

现在就使用 npm run build 进行打包了

在 package.json 中定义的脚本会优先在本地查找运行

loader

webpack 可以使用 loader 来预处理文件。这允许你打包除 JavaScript 之外的任何静态资源。

注意:使用多个 loader 时,加载顺序从右至左
**
css-loader

解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码(只负责加载)

安装

npm install —save-dev css-loader

配置

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        // 匹配规则,匹配 .css 为扩展名的文件
        test: /\.css$/,
        // style-loader 负责将样式添加到 DOM 中
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  }
}

style-loader
_
将模块的导出作为样式添加到 DOM 中

安装

npm install style-loader —save-dev

配置

// webpack.config.js
{
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // css-loader 负责将 css 文件进行加载
          { loader: "style-loader" },
          { loader: "css-loader" }
        ]
      }
    ]
  }
}

less-loader

加载和转译 LESS 文件

安装

npm install —save-dev less-loader less

less-loader 需要 less依赖,所以同时需要安装 less

配置

// webpack.config.js
module.exports = {
    ...
    module: {
        rules: [{
            test: /\.less$/,
              // 有顺序要求
            use: [{
                loader: "style-loader"  // creates style nodes from JS strings
            }, {
                loader: "css-loader"  // 将 CSS 转换为 CommonJS(将 CSS 导入到 js 文件中)
            }, {
                loader: "less-loader"  // creates style nodes from JS strings
            }]
        }]
    }
};

url-loader

像 file loader 一样工作,但如果文件小于限制,可以返回 data URL(base64)

安装

npm install —save-dev url-loader

配置

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif|jpeg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
            // 限制图片大小(单位 byte),小于这个大小的图片会转为 base64
            // 当加载的图片大于 limit 时,需要使用 file-loader 模块进行加载
            // file-loader 不需要配置
            // 当大于 limit 时 file-loader 会自动将图片打包到出口(output)路径
            limit: 8192,
            // 对生成的文件进行命名规范
            // 文件夹/图片原本的名称.截取 hash 值为 8 位.原本的扩展名
            name: 'img/[name].[hash:8].[ext]'
            }
          }
        ]
      }
    ]
  }
}

publicPath 配置

module.exports = {
  output: {
    // 只要涉及到 url(静态文件),会自动在路径前面加上 publicPath
    publicPath: './dist/'
  }
}

babel-loader

这个 loader 可以对 ES6 语法处理,转换为 ES5

安装

npm install —save-dev babel-loader@7 babel-core babel-preset-es2015

babel-core 是 babel-loader 的依赖,babel-preset-es2015 则是转换成 ES5 需要安装的,如果是转换成 TypeScript 则需要安装其他

配置

// webpack.config.js
module: {
  rules: [
    {
      // 匹配 .js 文件
      test: /\.js$/,
      // 排除不需要转换的文件
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['es2015']
        }
      }
    }
  ]
}

配置 Vue

安装 Vue

npm install —save vue

配置 Vue

// webpack.config.js
// vue 有两个版本
// 1.runtime-only  代码中不可以有任何的 template
// 2.runtime-compiler  代码中可以有 template
resolve: {
  alias: {
  // 指定使用 node_modules 中 Vue 的文件
  // 默认是使用 vue.runtime.js
  'vue$': 'vue/dist/vue.esm.js'
  }
}

安装 loader

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

vue-loader 用于加载 .vue 文件,vue-template-compiler 用于编译 .vue 文件(两个 loader 的版本需要一致)

配置 loader

// webpack.config.js
module: {
  rules: [
    {
      test: /\.vue$/,
      use: ['vue-loader']
    }
  ]
}

Vue Loader v15 现在需要配合一个 webpack 插件才能正确使用:

// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  // ...
  plugins: [
    new VueLoaderPlugin()
  ]
}

配置完后就能使用 .vue 的文件了

示 例

// main.js
// 加载 Vue
import Vue from 'vue';

// 将模版和数据提取到 .vue 文件并分离(模板和数据进行了分离)
import App from './vue/app.vue'

new Vue({
  el: '#app',
  // template 会把 html 中的 <div id="app"><div> 替换掉
  template: '<App/>',
  components: {
    App
  }
})
// app.vue
<template>
  <div>
    <h2 class="title">{{ message }}</h2>
    <button>按钮</button>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      message: "Hello webpack",
    };
  },
};
</script>

<style>
.title {
  color: green;
}
</style>

plugin

BannerPlugin

为打包的文件添加版权声名
_
配置

// webpack.config.js
// 使用 webpack 内置的 plugin 需要导入 webpack
const webpack = require('webpack')
module.exports = {
  plugins: [
    new webpack.BannerPlugin('最终版权归xxx所有')
  ]
}

html-webpack-plugin

将 index.html 打包到 dist 文件夹中,并且会自动引入打包的 js 文件
_
安装

npm install —save-dev html-webpack-plugin

配置

// webpack.config.js
// 导入 html-webpakc-plugin 插件
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  plugins: [
    new htmlWebpackPlugin({
      // 指定模版生成 index.html
      template: 'index.html'
    })
  ]
}

由于 index.html 会自动打包到 dist 中,module.exports.publicPath 就是多余的了

uglifyjs-webpack-plugin

压缩 js 文件(压缩后会删除注释,开发中时并不适合压缩文件,调试时候不方便)

安装_

npm install —save-dev uglifyjs-webpack-plugin@1.1.1

版本号指定 1.1.1,和 CLI2 保持一致(官方没有使用 webpack 自带的压缩插件)
_
配置

// webpack.config.js
// 导入 uglifyjs-webpack-plugin 插件
const uglifyjsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
  plugins: [
    new uglifyjsPlugin()
  ]
}

webpack-dev-serve

开启本地服务器
_
安装

npm install —save-dev webpack-dev-server@2.9.3

将开启服务命令映射到 package.json

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  // --open 执行并打开
  "dev": "webpack-dev-server --open"
}

配置

// webpack.config.js
module.exports = {
  devServer: {
    // 需要服务的文件夹
    contentBase: './dist',
    // 是否实时进行监听
    inline: true,
    // 默认运行 8080 端口
    // port:端口号
  }
}

配置分离

像一些 uglifyjs-webpack-plugin 脚本在开发时并不会使用,而 webpack-dev-serve 脚本却只在生产环境时会使用到,所以我们可以让开发时和生产时的环境配置分离。

配置文件分离后合并需要用到 webpack-merge 插件

安装 webpack-merge

npm install webpack-merge —save-dev

新建一个 build 文件夹,并创建 base.config.js、dev.config.js 和 prod.config.js 文件

分离开发时配置文件

// dev.config.js
// 导入合并配置文件要用的插件
const webpackMerge = require('webpack-merge');
// 导入需要合并的配置文件
const baseConfig = require('./base.config');

module.exports = webpackMerge(baseConfig, {
  // 只在开发阶段使用
  devServer: {
    contentBase: './dist',
    inline: true,
    // 默认运行 8080 端口
    // port:端口号
  }
})

当文件路径发生改变时需要在 package.json 中指定运行路径

// package.json
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "dev": "webpack-dev-server --open --config ./build/dev.config.js"
}

分离生产时配置文件

// prod.config.js
// 导入 uglifyjs-webpack-plugin 插件
const uglifyjsPlugin = require('uglifyjs-webpack-plugin');
// 导入合并配置文件要用来的插件
const webpackMerge = require('webpack-merge');
// 导入需要合并的配置文件
const baseConfig = require('./base.config');

module.exports = webpackMerge(baseConfig, {
  plugins: [
    new uglifyjsPlugin()
  ]
})

当文件路径发生改变时需要在 package.json 中指定运行路径

// package.json
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "build": "webpack --config ./build/prod.config.js"
}

文件别名

给文件起别名可以更好地查找文件

用 import 加载文件时,可以在路径中正常使用别名,但在 src 中时,需要在别名前加 ~ 符号

module.exports = {
  resolve: {
    alias: {
      // @ 会自动查找 src 的路径
      // @/img 相当于 src 下的 img
      '@': resolve('src'),
    }
  },
}

Vue CLI2

使用

安装 Vue 脚手架

npm install -g @vue/cli

上面安装的 Vue CLI3 的版本,如果需要想按照 Vue CLI2 的方式初始化项目时需要安装一个格拉工具

拉取 2.x 模板(旧版本) npm install -g @vue/cli-init

Vue CLI2 初始化项目

vue init webpack projectName

image.png

目录结构

│  .babelrc
│  .editorconfig
│  .eslintignore
│  .eslintrc.js
│  .gitignore
│  .postcssrc.js
│  filedir.txt
│  index.html
│  package-lock.json
│  package.json
│  README.md
│  
├─build
│      build.js
│      check-versions.js
│      logo.png
│      utils.js
│      vue-loader.conf.js
│      webpack.base.conf.js
│      webpack.dev.conf.js
│      webpack.prod.conf.js
│      
├─config
│      dev.env.js
│      index.js
│      prod.env.js
│           
├─src
│  │  App.vue
│  │  main.js
│  │  
│  ├─assets
│  │      logo.png
│  │      
│  └─components
│          HelloWorld.vue
│          
└─static
        .gitkeep

image.png
package.json

  • build 脚本

node build/build.js 运行 build 下的 build.js 文件

build.js 中 引入了 webpack.prod.conf 生产时环境配置

webpack.prod.conf 中合并了基础环境配置 webpack.base.conf.js

  • dev 脚本

webpack-dev-server —inline —progress —config build/webpack.dev.conf.js
基于 webpack-dev-sever 插件开启本地服务器,并指定了配置文件为 build/webpack.dev.conf.js

build/webpack.dev.conf.js 中合并了基础环境配置 webpack.base.conf.js

config

index.js 定义了配置文件中用到的变量

src

开发时文件夹

static

存放静态文件(发布时会原封不动复制到 dist 文件夹中)

.editorconfig

项目文本相关配置

# 当 root 为 true 时才会解析该文件
root = true

[*]
charset = utf-8
# 缩进风格
indent_style = space
# 缩进的大小为 2 个空格
indent_size = 2
# 在行尾换行
end_of_line = lf
# 在最后插入一个新的行
insert_final_newline = true
# 清除无效的空格
trim_trailing_whitespace = true

.eslintignore
**
忽略 eslint 检测的配置文件

编译模式

image.png
一、问题

用 vue cli2 创建项目时,会让我们选择 runtime-compiler 还是 runtime-only
image.png
二、结论

  • runtime-only 比 runtime-compiler 轻 6kb
  • runtime-only 运行更快
  • runtime-only 只能识别 render 函数,不能识别 template,.vue 文件通过 vue-template-compiler 编译成 render 函数,所以只能在 .vue 文件里写 template

三、解释
**
runtim-compiler 的运行过程

template -> ast -> render -> virtual dom -> 真实 dom

runtim-only 的运行过程

render -> virtual dom -> 真实 dom

由于 runtim-only 比 runtim-compiler 少的两个过程,所以性能更高、源代码量更少(小 6kb)

两种模式生成的代码区别只在 main.js 中:
image.pngimage.png
h => h(App) 中的 h 实际上是 createElement 函数,用于创建虚拟 dom

createElement 函数的语法:

createElement(‘标签’, { 标签的属性 }, [ ‘标签的内容’ ]) createElement(组件对象)

示例1

编译前:

// 传入标签的用法
new Vue({
  el: '#app',
  // render: h => h(App)
  render: function (createElement) {
    return createElement('h2', { class: 'box' }, ['Hello World'])
  }
})

// createElement 的第二个参数是可以省略的, 并且可以添加子元素
return createElement('h2', 
  { class: 'box' }, 
  ['Hello World', createElement('button', ['按钮'])])

编译后:

<!-- index.html -->
<!-- 替换了原本的 <div id="app"></div> -->
<h2 class="box">Hello World</h2>

<h2 class="box">Hello World
  <button>按钮</button>
</h2>

示例2

编译前

const cpn = {
  template: `<div>{{message}}</div>`,
  data() {
    return {
      message: '我是组件的 message'
    }
}

new Vue({
  el: '#app',
  // render: h => h(App)
  render: function (createElement) {
    return createElement(cpn)
  }
})

编译后

<!-- index.html -->
<!-- 替换了原本的 <div id="app"></div> -->
<div>我是组件的 message</div>

所以可以将 runtim-only 源码中的 App 作为模板传入了 createElement() 中
image.png
虽然 App.vue 文件中依然是有 template
image.png
但是当 vue 编译后,template 会渲染成 render 函数(由 vue-template-compiler 此 loader 编译),成为一个对象

build 和 dev

npm run build 运行过程
image.png
npm run dev 运行过程
image.png

打包文件解析

I4T7NQUEHB$8QCMU47`M67N.png

Vue CLI3

与 vue-cli 2 的区别

  • vue-cli 3 是基于 webpack 4 打造,vue-cli2 还是 webpack 3
  • vue-cli 3 的设计原则是”0配置”, 移除了配置文件根目录下的, build 和 config 等目录
  • vue-cli 3 提供了 vue ui 命令,提供了可视化配置,更加人性化
  • 移除了 static 文件夹,新增了 public 文件夹,并且 index.html 移动到 public中

使用

Vue CLI3 初始化项目

vue create projectName

image.png

目录结构

image.png

配置文件

  • vue ui

使用 vue ui 命令启动可视化本地项目管理器服务(任何目录下执行都可以)

导入或创建项目进行配置管理

image.png

  • 自定义配置

在项目根目录下创建 vue.config.js,然后创建对应相关的配置,最终会和其它配置合并

babelrc 和 babel.config.js

  • babelrc 只会影响本项目中的代码,
  • babel.config.js 会影响整个项目中的代码,包含 node_modules中的代码
  • babel.config.js 是按照 commonjs 导出对象,可以写 js 的逻辑
  • 一般有了babel.config.js 就不会在去执行.babelrc的设置