webpack实战之(手把手教你从0开始搭建一个vue项目)

前言

我们前面从前端架构:

到项目工程化工具eslint、babel、webpack等,分别结合demo跟源码做了具体的分析:

接下来我们把前面的所有知识点来实战一下,就当成前面所有知识点的一个总结了。

需求

我们都知道,当我们用react开发一个项目的时候,我们可以用官方的脚手架Create React App,当我们用vue开发一个项目的时候,我们也可以用官方的脚手架vue-cli, 用起来是真的爽啊!也不需要自己去配置eslint、babel、webpack等等,脚手架会根据你的选择自动帮你完成配置,是的! 一般项目都可以覆盖到,但是如果想要用好这些脚手架的话我们还是需要了解其中的一些配置的原理的。

我们这一次实战demo的需求是这样的:

  • js框架(vue、typescript、tsx、jsx)
  • css样式(sass)
  • 工程化工具(eslint、babel、webpack、webpack-chain)

ok!这差不多算是一个比较复杂的项目了,接下来我们一步步来实现一下它。

开始

我们直接创建一个项目叫webpack-vue-demo,然后进到项目根目录执行npm init:

webpack-vue-demo npm init
    ...
Is this OK? (yes) 
➜  webpack-vue-demo 

webpack脚手架

创建完毕后,我们首先安装webpack相关依赖:

webpack(webpack框架)

yarn add -D webpack || npm install -D webpack

webpack-cli(webpack脚手架)

yarn add -D webpack-cli || npm install -D webpack-cli

webpack-dev-server(webpack开发环境)

yarn add -D webpack-dev-server || npm install -D webpack-dev-server

webpack-chain(webpack链式配置)

yarn add -D webpack-chain || npm install -D webpack-chain

ok! webpack脚手架相关的依赖我们算是安装完了,然后我们在工程根目录底下创建一个webpack配置文件webpack.config.js,用webpack-chain导出一个配置:

const config = new (require("webpack-chain"))();
module.exports = config.toConfig();

然后我们就可以在根目录执行npx webpacknpx webpack-dev-server命令了,为了方便,我们直接在package.json中声明两个脚本builddev,

{
  "name": "webpack-vue-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "rimraf dist && webpack --mode=production",
    "dev": "webpack-dev-server --mode=development --progress"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.44.0",
    "webpack-chain": "^6.5.1",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  }
}

我们试着运行一下build命令:

> webpack-vue-demo@1.0.0 build xx/webpack/webpack-vue-demo
> rimraf dist && webpack --mode=production


Insufficient number of arguments or no entry found.
Alternatively, run 'webpack(-cli) --help' for usage info.

Hash: a8dc1c75fbec1af4c624
Version: webpack 4.44.0
Time: 29ms
Built at: 07/27/2020 4:45:12 PM

ERROR in Entry module not found: Error: Can't resolve './src' in 'xxx/webpack/webpack-vue-demo'
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! webpack-vue-demo@1.0.0 build: `rimraf dist && webpack --mode=production`
npm ERR! Exit status 2
npm ERR! 
npm ERR! Failed at the webpack-vue-demo@1.0.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
➜  webpack-vue-demo git:(master) ✗ 

可以看到,webpack直接报错了~ ok! 原因我就不解释了,没看过前面文章的童鞋强烈建议先看完再过来实战。

typescript

因为我们需求中需要支持ts,所以我们继续安装ts相关依赖:

typescript(ts脚手架)

yarn add -D typescript || npm install -D typescript

ts-loader(ts加载器)

yarn add -D ts-loader || npm install -D ts-loader

ok! ts相关依赖安装完毕后,我们可以开始创建我们项目的目录了,我们直接在根目录底下创建一个src目录,然后在src目录下创建一个main.ts空文件当成项目的入口文件:

webpack-vue-demo
    node_modules
    src
        main.ts
    .gitignore
    package.json
    README.md
    webpack.config.js

然后我们去修改一下webpack的配置。

入口配置

webpack.config.js:

const path = require("path");
const config = new (require("webpack-chain"))();
config
    .context(path.resolve(__dirname, ".")) //webpack上下文目录为项目根目录
    .entry("app") //入口文件名称为app
        .add("./src/main.ts") //入口文件为./src/main.ts
        .end()
    .output
        .path(path.join(__dirname,"./dist")) //webpack输出的目录为根目录的dist目录
        .filename( "[name].[contenthash:8].js") //打包出来的bundle名称为[name].[contenthash:8].js
        .publicPath("./") //publicpath配置为"./"
    .end()

module.exports = config.toConfig();

我们再次执行npm run build

➜  webpack-vue-demo git:(master) ✗ npm run build

> webpack-vue-demo@1.0.0 build xxx/webpack/webpack-vue-demo
> rimraf dist && webpack --mode=production

Hash: f102af84da880d301fb5
Version: webpack 4.44.0
Time: 198ms
Built at: 07/27/2020 5:05:26 PM
          Asset       Size  Chunks                         Chunk Names
app.995b67bc.js  964 bytes       0  [emitted] [immutable]  app
Entrypoint app = app.995b67bc.js
[0] multi ./src/main.ts 28 bytes {0} [built]
[1] ./src/main.ts 0 bytes {0} [built]
➜  webpack-vue-demo git:(master) ✗ 

ok,这一次我们可以看到,webpack通过我们配置的入口文件main.ts文件打包出来了一个叫app.995b67bc.js的文件,

dist/app.995b67bc.js:

!function (e) {
    var t = {};

    function n(r) {
        if (t[r]) return t[r].exports;
        var o = t[r] = {i: r, l: !1, exports: {}};
        return e[r].call(o.exports, o, o.exports, n), o.l = !0, o.exports
    }

    n.m = e, n.c = t, n.d = function (e, t, r) {
        n.o(e, t) || Object.defineProperty(e, t, {enumerable: !0, get: r})
    }, n.r = function (e) {
        "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {value: "Module"}), Object.defineProperty(e, "__esModule", {value: !0})
    }, n.t = function (e, t) {
        if (1 & t && (e = n(e)), 8 & t) return e;
        if (4 & t && "object" == typeof e && e && e.__esModule) return e;
        var r = Object.create(null);
        if (n.r(r), Object.defineProperty(r, "default", {
            enumerable: !0,
            value: e
        }), 2 & t && "string" != typeof e) for (var o in e) n.d(r, o, function (t) {
            return e[t]
        }.bind(null, o));
        return r
    }, n.n = function (e) {
        var t = e && e.__esModule ? function () {
            return e.default
        } : function () {
            return e
        };
        return n.d(t, "a", t), t
    }, n.o = function (e, t) {
        return Object.prototype.hasOwnProperty.call(e, t)
    }, n.p = "./", n(n.s = 0)
}([function (e, t, n) {
    e.exports = n(1)
}, function (e, t) {
}]);

哈哈,估计你是看不懂的! 因为我们的main.ts是一个空文件,所以打包出来的文件只会包含一些webpack的runtime代码。

我们试着去main.ts文件中写点ts代码,

src/main.ts:

let a:string="hello";

我们再次运行npm run build

➜  webpack-vue-demo git:(master) ✗ npm run build

...
ERROR in ./src/main.ts 1:5
Module parse failed: Unexpected token (1:5)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> let a:string="hello";
 @ multi ./src/main.ts app[0]
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! webpack-vue-demo@1.0.0 build: `rimraf dist && webpack --mode=production`
npm ERR! Exit status 2
npm ERR! 
npm ERR! Failed at the webpack-vue-demo@1.0.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
➜  webpack-vue-demo git:(master) ✗ 

可以看到,webpack直接报错了,因为webpack默认的loader只能加载js跟json语法,不能识别我们的ts语法,所以webpack建议我们配置.ts结尾文件的loader。

配置ts-loader

webpack.config.js:

const path = require("path");
const config = new (require("webpack-chain"))();
config
    .context(path.resolve(__dirname, ".")) //webpack上下文目录为项目根目录
    .entry("app") //入口文件名称为app
        .add("./src/main.ts") //入口文件为./src/main.ts
        .end()
    .output
        .path(path.join(__dirname,"./dist")) //webpack输出的目录为根目录的dist目录
        .filename( "[name].[contenthash:8].js") //打包出来的bundle名称为[name].[contenthash:8].js
        .publicPath("./") //publicpath配置为"./"
        .end()
    .resolve
        .extensions
            .add(".js").add(".jsx").add(".ts").add(".tsx").add(".vue") //配置以.js等结尾的文件当模块使用的时候都可以省略后缀
            .end()
        .end()
    .module
        .rule("type-script")
            .test(/\.tsx?$/) //loader加载的条件是ts或tsx后缀的文件
            .use("ts-loader")
                .loader("ts-loader")
                .options({ //ts-loader相关配置
                    transpileOnly: true,
                    appendTsSuffixTo: ['\\.vue$']
                })
                .end()
            .end()
module.exports = config.toConfig();

然后在根目录创建一个ts的配置文件tsconfig.json:

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "suppressImplicitAnyIndexErrors": true,
    "resolveJsonModule": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "webpack-env"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
  ],
  "exclude": [
    "node_modules"
  ]
}

ok,然后我们再次运行npm run build命令:

➜  webpack-vue-demo git:(master) ✗ npm run build

> webpack-vue-demo@1.0.0 build xxx/webpack-vue-demo
> rimraf dist && webpack --mode=production

Hash: f5bd51439408213acbe2
Version: webpack 4.44.0
Time: 403ms
Built at: 07/27/2020 5:21:40 PM
          Asset       Size  Chunks                         Chunk Names
app.b894f269.js  999 bytes       0  [emitted] [immutable]  app
Entrypoint app = app.b894f269.js
[0] multi ./src/main.ts 28 bytes {0} [built]
[1] ./src/main.ts 47 bytes {0} [built]
➜  webpack-vue-demo git:(master) ✗ 

可以看到,这次就没有报错了,因为我们的webpack可以通过我们定义的ts-loader识别我们的ts语法了,我们来验证一下吧,我们修改main.ts文件:

let a:string="hello";
console.log(a)

代码很简单,然后我们执行"npm run build"看最后打包出来的文件:

dist/app.b894f269.js

!function (e) {
    var t = {};

   ...
}([function (e, t, n) {
    e.exports = n(1)
}, function (e, t, n) {
    "use strict";
    console.log("hello") //webpack打包过后的代码
}]);

vue配置

vue

yarn add vue || npm install -S vue

vue-loader(vue加载器)

yarn add -D vue-loader || npm install -D vue-loader

vue-template-compiler(.vue文件模版编译器)

yarn add -D vue-template-compiler || npm install -D vue-template-compiler

vue-class-component(vue类组件)

yarn add  vue-class-component || npm install -S vue-class-component

vue-property-decorator(vue属性装饰器)

yarn add  vue-property-decorator || npm install vue-property-decorator

ok, 然后我们修改一下配置文件,让webpack支持.vue文件解析,

webpack.config.js:

const path = require("path");
const config = new (require("webpack-chain"))();
config
    .context(path.resolve(__dirname, ".")) //webpack上下文目录为项目根目录
    .entry("app") //入口文件名称为app
        .add("./src/main.ts") //入口文件为./src/main.ts
        .end()
    .output
        .path(path.join(__dirname,"./dist")) //webpack输出的目录为根目录的dist目录
        .filename("[name].[hash:8].js")
        .end()
    .resolve
        .extensions
            .add(".js").add(".jsx").add(".ts").add(".tsx").add(".vue") //配置以.js等结尾的文件当模块使用的时候都可以省略后缀
            .end()
        .end()
    .module
        .rule("type-script")
            .test(/\.tsx?$/) //loader加载的条件是ts或tsx后缀的文件
            .use("ts-loader")
                .loader("ts-loader")
                .options({ //ts-loader相关配置
                    transpileOnly: true,
                    appendTsSuffixTo: ['\\.vue$']
                })
                .end()
            .end()
        .rule("vue")
            .test(/\.vue$/)// 匹配.vue文件
                .use("vue-loader")
                .loader("vue-loader")
                .end()
            .end()
        .end()
    .plugin("vue-loader-plugin")//vue-loader必须要添加vue-loader-plugin
        .use(require("vue-loader").VueLoaderPlugin,[])
        .end()
    .devServer
        .host("0.0.0.0") //为了让外部服务访问
        .port(8090) //当前端口号
        .hot(true) //热载
        .open(true) //开启页面
module.exports = config.toConfig();

然后我们去src目录下创建一个app.vue文件用来测试,

src/app.vue:

<template>
    <div>{{this.msg}}</div>
</template>

<script lang="ts">
  import {Vue, Component} from "vue-property-decorator";

  @Component
  export default class App extends Vue {
    msg="hello world"
  }
</script>

<style scoped>

</style>

很简单,就是输出一个"hello world"到页面,然后我们修改一下“main.ts”文件,

src/main.ts:

import Vue from "vue";
import App from "./app.vue";

new Vue({
  el: "#app",
  render: (h) => h(App)
});

ok! 我们直接运行一下npm run dev:

192:webpack-vue-demo yinqingyang$ npm run dev

> webpack-vue-demo@1.0.0 dev xxx/webpack-vue-demo
> webpack-dev-server --mode=development --progress

10% building 2/2 modules 0 activeℹ 「wds」: Project is running at http://0.0.0.0:8090/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from xxx/xxx/webpack-vue-demo
10% building 5/15 modules 10 activexxx/webpack-vue-demo/node_modules/webpack-dev-server/client/utils/createSocketUrℹ 「wdm」: wait until bundle finished: /
ℹ 「wdm」: Hash: 6b8de437340cfe2ceccd
Version: webpack 4.44.0
Time: 2431ms
Built at: 07/27/2020 8:57:11 PM
          Asset     Size  Chunks                         Chunk Names
app.6b8de437.js  697 KiB     app  [emitted] [immutable]  app
Entrypoint app = app.6b8de437.js
...
[./src/main.ts] 108 bytes {app} [built]
    + 35 hidden modules
ℹ 「wdm」: Compiled successfully.


可以看到,webpack-dev-server服务已经开启了,并且服务器在8090端口监听,我们直接浏览器打开链接:http://0.0.0.0:8090/webpack-dev-server

在这里插入图片描述

webpack-dev-server会把所有生成的文件列在这里,“app.6b8de437.js”就是webpack根据入口main.ts文件生成的bundle文件。

接下来我们创建一个public目录,然后在public目录中创建一个叫index.html的文件用来测试,

public/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<noscript>your browser should support javascript!</noscript>
<div id="app"></div>
<script src="http://0.0.0.0:8090/app.6b8de437.js"></script>
</body>
</html>

ok,可以看到,我们直接放了一个div元素供main.ts中的vue实例挂载,然后就是直接一个script标签加载入口文件,我们直接把index.html文件在浏览器打开:

在这里插入图片描述

可以看到,浏览器中显示了我们的预期结果! 但是我们是手动的去引入“app.6b8de437.js”文件的,这难免有点不方便,所以接下来我们让它自动导入bundle文件到模版html文件中。

html-webpack-plugin

我们首先安装html-webpack-plugin:

yarn add -D html-webpack-plugin || npm install -D html-webpack-plugin

然后修改一下配置文件添加html-webpack-plugin插件,

webpack.config.js:

const path = require("path");
const config = new (require("webpack-chain"))();
config
    .context(path.resolve(__dirname, ".")) //webpack上下文目录为项目根目录
    .entry("app") //入口文件名称为app
        .add("./src/main.ts") //入口文件为./src/main.ts
        .end()
    .output
        .path(path.join(__dirname,"./dist")) //webpack输出的目录为根目录的dist目录
        .filename("[name].[hash:8].js")
        .end()
    .resolve
        .extensions
            .add(".js").add(".jsx").add(".ts").add(".tsx").add(".vue") //配置以.js等结尾的文件当模块使用的时候都可以省略后缀
            .end()
        .end()
    .module
        .rule("type-script")
            .test(/\.tsx?$/) //loader加载的条件是ts或tsx后缀的文件
            .use("ts-loader")
                .loader("ts-loader")
                .options({ //ts-loader相关配置
                    transpileOnly: true,
                    appendTsSuffixTo: ['\\.vue$']
                })
                .end()
            .end()
        .rule("vue")
            .test(/\.vue$/)// 匹配.vue文件
                .use("vue-loader")
                .loader("vue-loader")
                .end()
            .end()
        .end()
    .plugin("vue-loader-plugin")//vue-loader必须要添加vue-loader-plugin
        .use(require("vue-loader").VueLoaderPlugin,[])
        .end()
    .plugin("html")// 添加html-webpack-plugin插件
        .use(require("html-webpack-plugin"),[{
            template: path.resolve(__dirname,"./public/index.html"), //指定模版文件
            chunks:["app"], //指定需要加载的chunk
            inject: "body" //指定script脚本注入的位置为body
        }])
        .end()
    .devServer
        .host("0.0.0.0") //为了让外部服务访问
        .port(8090) //当前端口号
        .hot(true) //热载
        .open(true) //开启页面
module.exports = config.toConfig();

ok,然后我们直接npm run dev:

npm run dev

运行后会发现浏览器自动打开了http://0.0.0.0:8090/页面:

在这里插入图片描述

虽然是运行成功了,但是ide一直在提示有个地方报错:

在这里插入图片描述

意思就是:“导入的app.vue并没有类型声明文件”,ok,我们直接在src目录底下创建一个shims-vue.d.ts文件用来声明.vue 文件导出的类型,

src/shims-vue.d.ts:

declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}

我们直接声明了一个module,然后导出一个vue实例,声明之后就会发现ide报错没有了。

css样式(scss)

我们试着在vue模版中使用一下sass语法:

src/app.vue

<template>
    <div class="app-container">{{this.msg}}</div>
</template>

<script lang="ts">
  import {Vue, Component} from "vue-property-decorator";

  @Component
  export default class App extends Vue {
    msg="hello world"
  }
</script>

<style scoped lang="scss">
.app-container{
    color: red;
}
</style>
...
File was processed with these loaders:
 * ./node_modules/vue-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
| 
| 
> .app-container{
|     color: red;
| }
 @ ./src/app.vue?vue&type=style&index=0&id=5ef48958&scoped=true&lang=scss& 1:0-148 1:164-167 1:169-314 1:169-314
 @ ./src/app.vue
 @ ./src/main.ts
 @ multi ./src/main.ts
ℹ 「wdm」: Failed to compile.

ok, 开启webpack服务后会发现报错了:“找不到能够解析当前样式语法的loader”,那我们接下来就让webpack支持scss语法。

sass(scss&&sass语法)

yarn add -D sass || npm install -D sass

sass-loader(sass&&scss文件加载器)

yarn add -D sass-loader || npm install -D sass-loader

postcss-loader(css样式处理工具)

比如:自动添加浏览器适配前缀、压缩css样式等等

yarn add -D postcss-loader || npm install -D postcss-loader
cssnano(压缩css)
yarn add -D cssnano || npm install -D cssnano
autoprefixer(自动添加浏览器适配前缀)
yarn add -D autoprefixer || npm install -D autoprefixer

css-loader(css模块加载器)

yarn add -D css-loader || npm install -D css-loader

mini-css-extract-plugin(抽离css样式到独立的.css文件)

yarn add -D mini-css-extract-plugin || npm install -D mini-css-extract-plugin

然后修改配置文件,webpack.config.js:

const path = require("path");
const config = new (require("webpack-chain"))();
const isDev = process.env.WEBPACK_DEV_SERVER;
config
    .context(path.resolve(__dirname, ".")) //webpack上下文目录为项目根目录
    .entry("app") //入口文件名称为app
        .add("./src/main.ts") //入口文件为./src/main.ts
        .end()
    .output
        .path(path.join(__dirname,"./dist")) //webpack输出的目录为根目录的dist目录
        .filename("[name].[hash:8].js")
        .end()
    .resolve
        .extensions
            .add(".js").add(".jsx").add(".ts").add(".tsx").add(".vue") //配置以.js等结尾的文件当模块使用的时候都可以省略后缀
            .end()
        .end()
    .module
        .rule("type-script")
            .test(/\.tsx?$/) //loader加载的条件是ts或tsx后缀的文件
            .use("ts-loader")
                .loader("ts-loader")
                .options({ //ts-loader相关配置
                    transpileOnly: true,
                    appendTsSuffixTo: ['\\.vue$']
                })
                .end()
            .end()
        .rule("vue")
            .test(/\.vue$/)// 匹配.vue文件
                .use("vue-loader")
                .loader("vue-loader")
                .end()
            .end()
        .rule("sass")
            .test( /\.(sass|scss)$/)//sass和scss文件
            .use("extract-loader")//提取css样式到单独css文件
                .loader(require('mini-css-extract-plugin').loader)
                .options({
                    hmr: isDev //开发环境开启热载
                })
                .end()
            .use("css-loader")//加载css模块
                .loader("css-loader")
                .end()
            .use("postcss-loader")//处理css样式
                .loader("postcss-loader")
                .options( {
                    config: {
                       path: path.resolve(__dirname, "./postcss.config.js")
                    }
                })
                .end()
            .use("sass-loader")//sass语法转css语法
                .loader("sass-loader")
                .end()
            .end()
        .end()
    .plugin("vue-loader-plugin")//vue-loader必须要添加vue-loader-plugin
        .use(require("vue-loader").VueLoaderPlugin,[])
        .end()
    .plugin("html")// 添加html-webpack-plugin插件
        .use(require("html-webpack-plugin"),[{
            template: path.resolve(__dirname,"./public/index.html"), //指定模版文件
            chunks:["app"], //指定需要加载的chunk
            inject: "body" //指定script脚本注入的位置为body
        }])
        .end()
    .plugin("extract-css")//提取css样式到单独css文件
        .use(require('mini-css-extract-plugin'), [{
            filename: "css/[name].css",
            chunkFilename: "css/[name].css"
        }])
        .end()
    .devServer
        .host("0.0.0.0") //为了让外部服务访问
        .port(8090) //当前端口号
        .hot(true) //热载
        .open(true) //开启页面
module.exports = config.toConfig();

然后我们给postcss指定配置文件,我们在根目录创建一个postcss.config.js:

module.exports = {
    plugins: [
        require("autoprefixer")(),
        require("cssnano")({
            preset: ['default', {
                mergeLonghand: false,
                cssDeclarationSorter: false
            }]
        })
    ]
};

可以看到,我们加入了“autoprefixer”跟“cssnano”插件用于自动添加浏览器前缀跟css压缩,然后我们在根目录创建一个.browserslistrc用于当前项目浏览器列表,

.browserslistrc:

> 0.25%, not dead

css样式相关的配置我这里就不详细说明了,不懂的小伙伴可以看前面的文章,配置完css后我们再看一下我们的app.vue文件,

src/app.vue:

<template>
    <div class="app-container">{{this.msg}}</div>
</template>

<script lang="ts">
  import {Vue, Component} from "vue-property-decorator";

  @Component
  export default class App extends Vue {
    msg="hello world"
  }
</script>

<style scoped lang="scss">
.app-container{
    color: green;
}
</style>

👌,可以看到,我们给了当前div一个样式:

.app-container{
    color: green;
}

然后我们运行npm run dev指令:

npm run dev
在这里插入图片描述

ok, 然后我们修改成为红色试试,src/app.vue:

<template>
    <div class="app-container">{{this.msg}}</div>
</template>

<script lang="ts">
  import {Vue, Component} from "vue-property-decorator";

  @Component
  export default class App extends Vue {
    msg="hello world"
  }
</script>

<style scoped lang="scss">
.app-container{
    color: red;
}
</style>
在这里插入图片描述

可以看到,当我们修改了样式后,mini-css-extract-plugin触发了热载,页面样式自动更新。

ok! 这一节先到这里了,因为扛不住了要睡觉了😂~ 下节继续~敬请期待!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335