webpack4配置Vue版脚手架六

38次阅读

共计 9706 个字符,预计需要花费 25 分钟才能阅读完成。

前言

系列文章

webpack4 从零开始构建(一)
webpack4+React16 项目构建(二)
webpack4 功能配置划分细化(三)
webpack4 引入 Ant Design 和 Typescript(四)
webpack4 代码去重, 简化信息和构建优化(五)
webpack4 配置 Vue 版脚手架(六)

因为之前我们已经花了五篇文章讲解了怎么从零配置一个 React 版的 Webpack 脚手架, 接下来我打算以前面代码为基础改成 Vue 版. 改变的第一步就是清除 React 全家桶和 Typescript 的痕迹.

清理工作

删除 node_modulestsconfig.json

用不上了

Package.json

删除不需要的依赖

{
  "sideEffects": [
    "*.scss",
    "*.css"
  ],
  "scripts": {
    "dev": "cross-env NODE_ENV=DEV webpack --config ./config/webpack.dev.js",
    "prod": "cross-env NODE_ENV=PROD webpack --config ./config/webpack.prod.js",
    "start": "cross-env NODE_ENV=SERVER webpack-dev-server --config ./config/webpack.server.js",
    "rnm": "rimraf node_modules"
  },
  "dependencies": { },
  "devDependencies": {
    "autoprefixer": "^9.4.10",
    "babel-core": "^6.26.3",
    "babel-loader": "7",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "clean-webpack-plugin": "^2.0.0",
    "cross-env": "^5.2.0",
    "css-loader": "^2.1.1",
    "file-loader": "^3.0.1",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "image-webpack-loader": "^4.6.0",
    "mini-css-extract-plugin": "^0.5.0",
    "node-sass": "^4.11.0",
    "optimize-css-assets-webpack-plugin": "^5.0.1",
    "postcss-loader": "^3.0.0",
    "progress-bar-webpack-plugin": "^1.12.1",
    "rimraf": "^2.6.3",
    "sass-loader": "^7.1.0",
    "source-map-loader": "^0.2.4",
    "style-loader": "^0.23.1",
    "url-loader": "^1.1.2",
    "webpack": "^4.30.0",
    "webpack-bundle-analyzer": "^3.1.0",
    "webpack-cli": "^3.2.3",
    "webpack-dev-server": "^3.2.1",
    "webpack-merge": "^4.2.1",
    "webpack-parallel-uglify-plugin": "^1.1.0",
    "xml-loader": "^1.2.1"
  },
  "name": "webpack_demo",
  "version": "1.0.0",
  "main": "index.tsx",
  "license": "MIT"
}

configrules.js

只保留基本的处理规则

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const {isProd, isServer} = require('./env')

const cssMiniLoader = !isServer
  ? {
    loader: MiniCssExtractPlugin.loader,
    options: {
      // you can specify a publicPath here
      // by default it use publicPath in webpackOptions.output
      publicPath: process.env.NODE_ENV === "DEV" ? "./" : "../"
    }
  }
  : "style-loader"; // 使用 <style> 将 css-loader 内部样式注入到我们的 HTML 页面,
const postcssLoader = {
  loader: "postcss-loader",
  options: {
    config: {path: "./" // 写到目录即可,文件名强制要求是 postcss.config.js}
  }
};
const imgLoader = {
  loader: "url-loader",
  options: {name: "[name].[hash:5].[ext]",
    limit: 20 * 1024, // size <= 50kb
    outputPath: "img"
  }
};

module.exports = [
  {
    test: /\.s?css$/, // 匹配文件
    use: [
      cssMiniLoader,
      "css-loader", // 加载.css 文件将其转换为 JS 模块
      postcssLoader,
      "sass-loader" // 加载 SASS / SCSS 文件并将其编译为 CSS
    ]
  },
  {test: /\.(png|svg|jpe?g|gif)$/i, // 图片处理
    use:
      isProd
        ? [
          imgLoader,
          {
            loader: "image-webpack-loader",
            options: {
              // Compress JPEG images
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              // Compress PNG images
              optipng: {enabled: false},
              //  Compress PNG images
              pngquant: {
                quality: "65-90",
                speed: 4
              },
              // Compress GIF images
              gifsicle: {interlaced: false}
            }
          }
        ]
        : [imgLoader]
  },
  {test: /\.(woff|woff2|eot|ttf|otf)$/, // 字体处理
    use: ["file-loader"]
  },
  {
    test: /\.xml$/, // 文件处理
    use: ["xml-loader"]
  },
  {test: /\.(html)$/,
    use: {loader: "html-loader"}
  }
];

src/*

保留目录, 除了图片和样式其他文件都删掉

安装基本依赖

yarn add vue vue-router vuex
yarn add --dev vue-loader vue-template-compiler

修改简化路径

const path = require("path");

// 创建 import 或 require 的别名,来确保模块引入变得更简单
module.exports = {"@": path.resolve(__dirname, "../src/"),
  IMG: path.resolve(__dirname, "../src/img"),
  STYLE: path.resolve(__dirname, "../src/style"),
  JS: path.resolve(__dirname, "../src/js"),
  ROUTER: path.resolve(__dirname, "../src/router"),
  VUEX: path.resolve(__dirname, "../src/vuex"),
  PAGE: path.resolve(__dirname, "../src/page"),
  CMT: path.resolve(__dirname, "../src/component"),
  // 'vue$':'vue/dist/vue.js'
};

最后一行大家可能有疑问, 如果不添加的话会这么输出

我们看 node_omdules 里 vue 仓库的 dist 目录, 里面有很多的构建版本

我们从它的 package.json 文件看到

"main": "dist/vue.runtime.common.js",
"module": "dist/vue.runtime.esm.js",
"unpkg": "dist/vue.js",
"jsdelivr": "dist/vue.js",

从官网我们找到这张图

术语 描述
完整版本(Full) 包含 编译器 (compiler)运行时 (runtime) 的构建版本
编译器(Compiler) 负责将模板字符串编译成 JavaScript render 函数的代码
运行时(Runtime) 负责 创建 Vue 实例 (creating Vue instances) 渲染 (rendering)修补虚拟 DOM(patching virtual DOM) 等的代码。基本上,等同于完整版本减去编译器
UMD UMD 构建版本能够直接在浏览器中通过 <script> 标签使用。jsDelivr CDN 提供的默认文件 https://cdn.jsdelivr.net/npm/vue,是运行时 + 编译器 (Runtime + Compiler) 的 UMD 构建版本(vue.js)
CommonJS CommonJS 版本用于较早期的打包器 (bundler)(例如 browserify 或 webpack 1 等) 中。用于这些打包器的默认文件 (pkg.main),是只含有运行时(Runtime only) 的 CommonJS 构建版本(vue.runtime.common.js)
ES Module ES 模块版本构建用于现代打包器 (例如 webpack 2 或 rollup 等) 中。用于这些打包器的默认文件 (pkg.module),是只含有运行时(Runtime only) 的 ES Module 构建版本(vue.runtime.esm.js)

在使用 vue-loadervueify 时,*.vue 文件中的模板会 在构建时 (build time) 预编译 (pre-compile) 为 JavaScript。最终生成的 bundle 中你不再需要编译器(compiler),因此可以直接使用只含有运行时的构建版本(runtime-only)。

所以我们只要改一下初始化的方式就没必要添加路径使用完整版, 具体方式下面 src\index.jsconfig/rules文件配置会提到

更加具体的解释可以直接查看不同构建版本的解释说明

创建组件

srcpageview1.vue

<template>
  <div>
    <p>Page1</p>
    <img
      class="img1"
      src='../img/1.jpg'
      alt=""
    />
  </div>
</template>

<script>
export default {};
</script>

<style lang="scss" scoped>
</style>

srcpageview2.vue

<template>
  <div>
    <p>Page2</p>
    <div class="img2" />
  </div>
</template>

<script>
export default {};
</script>

<style lang="scss" scoped>
</style>

srcApp.vue

<template>
  <div id="app">
    <router-link to="/view1">view1</router-link>
    <router-link to="/view2">view2</router-link>
    <router-view></router-view>
  </div>
</template>
<script>
export default {};
</script>

srcrouterindex.js

import Vue from 'vue';
import Router from 'vue-router';

Vue.use(Router);
let router = new Router({
  routes: [
    {
      // 首页
      path: '/view1',
      component: () => import('CMT/view1')
    },
    {
      path: '/view2',
      component: () => import('CMT/view2')
    },
    {path: '*', redirect: '/view1'}
  ]
});

export default router;

srcvuexindex.js

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({state: {},
  mutations: {}});

srcindex.js

// page
import 'STYLE/style.scss'
import Vue from 'vue';
import router from 'ROUTER/index.js';
import store from 'VUEX/index.js';
import App from './App';


new Vue({
  el: '#root',
  router,
  store,
  render: h => h(App)
});

修改 Webpack

configrules.js

主要变化是插入了对 ES 语法编译和 Vue 语法支持

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const {isProd, isServer} = require('./env')

const cssMiniLoader = !isServer
  ? {
    loader: MiniCssExtractPlugin.loader,
    options: {
      // you can specify a publicPath here
      // by default it use publicPath in webpackOptions.output
      publicPath: process.env.NODE_ENV === "DEV" ? "./" : "../"
    }
  }
  : "style-loader"; // 使用 <style> 将 css-loader 内部样式注入到我们的 HTML 页面,

const postcssLoader = {
  loader: "postcss-loader",
  options: {
    config: {path: "./" // 写到目录即可,文件名强制要求是 postcss.config.js}
  }
};

const imgLoader = {
  loader: "url-loader",
  options: {name: "[name].[hash:5].[ext]",
    limit: 20 * 1024, // size <= 50kb
    outputPath: "img"
  }
};

module.exports = [{
  test: /\.js$/,
  exclude: /node_modules/,
  use: {loader: 'babel-loader'}
},
{test: /\.vue$/, use: 'vue-loader'},
{
  test: /\.s?css$/, // 匹配文件
  use: [
    cssMiniLoader,
    "css-loader", // 加载.css 文件将其转换为 JS 模块
    postcssLoader,
    "sass-loader" // 加载 SASS / SCSS 文件并将其编译为 CSS
  ]
},
{test: /\.(png|svg|jpe?g|gif)$/i, // 图片处理
  use:
    isProd
      ? [
        imgLoader,
        {
          loader: "image-webpack-loader",
          options: {
            // Compress JPEG images
            mozjpeg: {
              progressive: true,
              quality: 65
            },
            // Compress PNG images
            optipng: {enabled: false},
            //  Compress PNG images
            pngquant: {
              quality: "65-90",
              speed: 4
            },
            // Compress GIF images
            gifsicle: {interlaced: false}
          }
        }
      ]
      : [imgLoader]
},
{test: /\.(woff|woff2|eot|ttf|otf)$/, // 字体处理
  use: ["file-loader"]
},
{
  test: /\.xml$/, // 文件处理
  use: ["xml-loader"]
},
{test: /\.(html)$/,
  use: {loader: "html-loader"}
}
];

configwebpack.common.js

修改对应后缀扩展

extensions: ['.js', '.vue', '.json', 'scss', 'css']

configenv.js

改变入口地址

const path = require('path');

const isDev = process.env.NODE_ENV !== "DEV",
  isProd = process.env.NODE_ENV !== "PROD",
  isServer = process.env.NODE_ENV !== "SERVER",

  entry = "./src/index.js",
  outputName = "[name].bundle.js",
  outputPath = path.resolve(__dirname, "../dist"),
  publicPath = "",
  
  title = "test";

module.exports = {
  isDev,
  isProd,
  isServer,
  entry,
  outputName,
  outputPath,
  publicPath,
  title
};

babelrc 配置详解

yarn add --dev babel-core babel-loader@7 babel-preset-env babel-preset-stage-2

之前只是粗略讲过它的一些基本情况, 现在可以单独讲讲它里面具体有什么东西, 除了之前说的

babel-core

是作为 babel 的核心, 把 javascript 代码分析成 AST (抽象语法树, 是源代码的抽象语法结构的树状表现形式),方便各个插件分析语法进行相应的处理

babel-loader

也是核心插件, 允许使用 Babel 和 webpack 转换 JavaScript 文件

初始的时候官方针对常用环境编写了一些 preset, 例如

preset 作用
babel-preset-es2015 可以将 es6 的代码编译成 es5
babel-preset-es2016 可以将 es7 的代码编译为 es6
babel-preset-es2017 可以将 es8 的代码编译为 es7
babel-preset-latest 支持现有所有 ECMAScript 版本的新特性

古老写法

babel-polyfill

完整模拟 ES2015+ 环境, 一次性引入所有模块并且同项目代码一起编译到生产环境。而且会污染全局变量, 增加体积大概在 200~300K 左右

babel-runtime

Babel 使用了非常小的 helpers 来实现诸如 _extend 等常用功能。默认情况下,它将被添加到每个通过 require 引用它的文件中。这种重复 (操作) 有时是不必要的,特别是当你的应用程序被拆分为多个文件时。

babel-plugin-transform-runtime

所有的 helper 都会引用模块 babel-runtime,以避免编译输出的重复问题。这个运行时会被编译到你的构建版本当中。另外一个目的就是为你的代码创建一个沙盒环境将内置插件起了别名

{
    // 插件
    "plugins": [
      [
        "transform-runtime",
        {"helpers": false, // 是否切换将内联 (inline) 的 Babel helper(classCallCheck,extends 等)替换为对 moduleName 的调用。"polyfill": false, // 是否切换新的内置插件 (Promise,Set,Map 等) 为使用非全局污染的 polyfill。"regenerator": true, // 是否切换 generator 函数为不污染全局作用域的 regenerator 运行时。"moduleName": "babel-runtime" // 当引入 helper 时,设置要使用的模块的名称 / 路径。}
      ]
    ],
}

新写法

babel-preset-env

但是随着历史进程, 越来越多的 preset 出现不便于开发配置, 于是推出了 babel-preset-env 基于你的实际浏览器及运行环境,自动的确定 babel 插件及 polyfills,转译 ES2015 及此版本以上的语言, 默认配置情况下和 babel-preset-latest 一致

我们直接配置兼容的版本情况支持每个浏览器最后两个版本和 safari 大于等于 7 版本所需的 polyfill 代码转换

但是 babel-preset-env 已提供方法可以替代上面 babel-plugin-transform-runtime 类似的作用了

// 预设
"presets": [
  [
    "env",
    {
      "modules": false,
      "targets": {
        "browsers": [
          "last 2 versions",
          "safari >= 7"
        ]
      },
      "useBuiltIns": "usage" // "usage" | "entry" | false, 默认为 false
    }
  ]
],

因为没有太深入研究, 具体有些差别不太清楚

babel-preset-stage-x

TC39 将提案分为以下几个阶段:

阶段 描述
Stage 0 – 设想(Strawman) 只是一个想法,可能有 Babel 插件
Stage 1 – 建议(Proposal) 这是值得跟进的
Stage 2 – 草案(Draft) 初始规范
Stage 3 – 候选(Candidate) 完成规范并在浏览器上初步实现
Stage 4 – 完成(Finished) 将添加到下一个年度版本发布中

因为 babel-preset-env 只支持最新推出版本的 JavaScript 语法(state-4), 所以如果想要体验部分还为完成阶段的新语法可以配置对应的插件转换, 一般主流推荐使用 2.

// 预设
"presets": [
  [
    "env",
    {
      "modules": false,
      "targets": {
        "browsers": [
          "last 2 versions",
          "safari >= 7"
        ]
      },
      "useBuiltIns": "usage" // "usage" | "entry" | false, 默认为 false
    }
  ]
  "stage-2"
],

还有部分暂时没用上的东西

{
  // 预设
  "presets": [
    [
      "env",
      {
        "modules": false,
        "targets": {
          "browsers": [
            "last 2 versions",
            "safari >= 7"
          ]
        },
        "useBuiltIns": "usage" // "usage" | "entry" | false, 默认为 false
      }
    ],
    "stage-2"
  ],
  // 插件
  "plugins": [],
  /* 
    设置特定的配置选项
    env 选项的值将从 process.env.BABEL_ENV 获取,如果没有的话,则获取 process.env.NODE_ENV 的值,它也无法获取时会设置为 "development" 
  */
  "env": {"development": {},
    "production": {}}
}

配置插件

如果以当前配置运行命令

yarn start

终端会输出错误如图

所以我们需要根据错误提示引入 Vue-loader 插件

configwebpack.common.js

const VueLoaderPlugin = require('vue-loader/lib/plugin');
------------------ 省略 -----------------------
plugins: [new VueLoaderPlugin(),
],

再次运行即可正常.

正文完
 0