前言

系列文章

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(),
],

再次运行即可正常.


Afterward
621 声望62 粉丝

努力去做,对的坚持,静待结果


引用和评论

0 条评论