我的身旁是一扇亮了又暗的窗

0%

Vue ToDo

初始化项目

1
npm init

安装需要的包

1
npm i webpack vue vue-loader

新建文件夹及文件

src/app.vue src/index.js webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div id="text">{{ text }}</div>
</template>

<script>
export default {
data() {
return {
text: "abce",
};
},
};
</script>

<style>
#text {
color: red;
}
</style>
1
2
3
4
5
6
7
8
9
import Vue from "vue";
import App from "./app.vue";

const root = document.createElement("div");
document.body.appendChild(root);

new Vue({
render: (h) => h(App),
}).$mount(root);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const path = require("path");
const webpack = require("webpack");

module.exports = {
target: "web",
entry: path.join(__dirname, "src/index.js"),
output: {
filename: "bundle.js",
path: path.join(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [new VueLoaderPlugin()],
};

补充安装

1
2
npm i css-loader
npm i postcss

添加运行

1
2
3
4
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js"
}
1
npm run build

异常 添加包

1
2
3
npm i vue-template-compiler

npm i style-loader

添加静态文件 图片及 CSS 文件位置

src/assets/images src/assets/styles

1
2
3
4
5
npm i url-loader

npm i file-loader

npm i stylus-loader

stylus

VScode 安装插件及设定配置
确保自动格式化时 stylus 的风格不发生变化,只需要对 vscode 进行设置,在 vscode 的插件选项中搜索 “stylus Supremacy” 安装,
然后修改 settings.json 文件:

修改过后就不会改变 stylus 的格式了

1
2
3
body
font-size: 20px
background-image: url('../images/92.png')

添加 webpack.config.js 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
module.exports = {
target: "web",
entry: path.join(__dirname, "src/index.js"),
output: {
filename: "bundle.js",
path: path.join(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.styl/,
use: ["style-loader", "css-loader", "stylus-loader"],
},
{
test: /\.(gif|jpg|jpeg|png|svg)$/,
use: [
{
loader: "url-loader",
options: {
limit: 1024,
name: "[name].[ext]",
},
},
],
},
],
},
plugins: [
new webpack.DefinePlugin({
"process.env": {
NODE_NEV: isDev ? '"development"' : '"production"',
},
}),
new VueLoaderPlugin(),
new HTMLPlugin(),
],
};

运行异常

Error: Cannot find module ‘stylus’
安装

1
npm i stylus

服务器运行

1
npm i webpack-dev-server

因为 Windoes 和 linux 的启动不一样添加包

1
npm i cross-env

因为主启动为 index.js 而服务器访问要为 index.html 还需要添加包

1
npm i html-webpack-plugin

修改 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const path = require("path");
const HTMLPlugin = require("html-webpack-plugin");
const webpack = require("webpack");

const isDev = process.env.NODE_NEV === "development";

const config = {
target: "web",
entry: path.join(__dirname, "src/index.js"),
output: {
filename: "bundle.js",
path: path.join(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.styl/,
use: ["style-loader", "css-loader", "stylus-loader"],
},
{
test: /\.(gif|jpg|jpeg|png|svg)$/,
use: [
{
loader: "url-loader",
options: {
limit: 1024,
name: "[name].[ext]",
},
},
],
},
],
},
plugins: [
//选择不同的状态
new webpack.DefinePlugin({
"process.env": {
NODE_NEV: isDev ? '"development"' : '"production"',
},
}),
new VueLoaderPlugin(),
//编译index.html
new HTMLPlugin(),
],
};

if (isDev) {
//方便调试
config.devtool = "#cheap-module-eval-source-map";
config.devServer = {
post: "8000",
host: "0.0.0.0",
overlay: {
errors: true,
},
//热替换
hot: true,
};
config.plugins.push(
//热替换插件
new webpack.HotModuleReplacementPlugin()
);
}

module.exports = config;

修改 package.json

1
2
3
4
5
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
"dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"
}

运行异常

npm WARN webpack-dev-middleware@3.7.2 requires a peer of webpack@^4.0.0 but
none is installed. You must install peer dependencies yourself.
更换 webpack 到合适项目的版本

1
2
3
npm uninstall webpack
//有一个包需要4.27.0所以选择这个
npm install webpack@4.27.0

运行异常

Error: Cannot find module ‘webpack-cli/bin/config-yargs’
不存在 webpack-cli/bin/config-yargs
安装 npm install webpack-cli 后还是报错
需要安装匹配的版本

1
npm install webpack-cli@3.3.12

创建 Vue 文件

在 src 创建新的文件夹及文件
src/todo/footer.jsx src/todo/header.vue src/todo/item.vue src/todo/tabs.vue src/todo/todo.vue

安装负责编译 jsx 文件的包

1
npm i postcss-loader autoprefixer babel-loader babel-core

babel-loader 版本过高

1
2
3
npm uninstall babel-loader

npm i babel-loader@7.1.5

让 Vue 支持 JSX

1
npm i babel-preset-env babel-plugin-transform-vue-jsx

缺少包

1
npm i babel-helper-vue-jsx-merge-props

创建文件.babelrc

1
2
3
4
{
"presets": ["env"],
"plugins": ["transform-vue-jsx"]
}

创建文件 postcss.config.js

1
2
3
4
5
const autoprofixer = require("autoprefixer");

module.exports = {
plugins: [autoprofixer()],
};

添加支持 jsx

1
npm i babel-plugin-syntax-jsx

编写文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import "../assets/styles/footer.styl";

export default {
data() {
return {
author: "MM",
};
},
render() {
return (
<div id="footer">
<span>Power by {this.author} </span>
</div>
);
},
};

header.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<header class="main-header">
<h1>TODO</h1>
</header>
</template>

<style lang="stylus" scoped>
.main-header
text-align: center
h1
font-size: 100px
background-image: linear-gradient(#167, #202d40)
-webkit-background-clip: text
text-fill-color: transparent
-webkit-text-fill-color: transparent
margin: 20px
</style>

item.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<template>
<div :class="['todo-item', todo.completed ? 'completed' : '']">
<input type="checkbox" class="toggle" v-model="todo.completed" />
<label>{{ todo.content }}</label>
<button class="destroy" @click="deleteTodo" />
</div>
</template>

<script>
export default {
props: {
todo: {
type: Object,
required: true,
},
},
methods: {
deleteTodo() {
this.$emit("del", this.todo.id);
},
},
};
</script>

<style lang="stylus" scoped>
.todo-item
position: relative
background-color: #fff
font-size: 24px
border-bottom: 1px solid rgba(0, 0, 0, 0.06)
&:hover
.destroy:after
content: '×'
label
white-space: pre-line
word-break: break-all
padding: 15px 60px 15px 15px
margin-left: 45px
display: block
line-height: 1.2
transition: color 0.4s
&.completed
label
color: #d9d9d9
text-decoration: line-through
.toggle
text-align: center
width: 40px
height: 40px
line-height: 40px
position: absolute
top: 0
bottom: 0
margin: auto 0
border: none
appearance: none
outline: none
padding-left: 5px
cursor: pointer
&:after
content: url('../assets/images/logo.png')
&:checked:after
content: url('../assets/images/logo.png')
.destroy
position: absolute
top: 0
right: 10px
bottom: 0
width: 40px
height: 40px
margin: auto 0
font-size: 30px
color: #cc9a9a
margin-bottom: 11px
transition: color 0.2s ease-out
background-color: transparent
appearance: none
border-width: 0
cursor: pointer
outline: none
</style>

tabs.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<template>
<div class="helper">
<span class="left"> {{ unFinishedTodoLength }} items left</span>
<span class="tabs">
<span
v-for="state in states"
:key="state"
:class="[state, filter === state ? 'actived' : '']"
@click="toggleFilter(state)"
>{{ state }}</span
>
</span>
<span class="clear" @click="clearAllCompleted">Clear Completed</span>
</div>
</template>

<script>
export default {
props: {
filter: {
type: String,
required: true,
},
todos: {
type: Array,
required: true,
},
},
data() {
return {
states: ["all", "active", "completed"],
};
},
computed: {
unFinishedTodoLength() {
return this.todos.filter((todo) => !todo.completed).length;
},
},
methods: {
clearAllCompleted() {
this.$emit("clearAllCompleted");
},
toggleFilter(state) {
this.$emit("toggleFilter", state);
},
},
};
</script>

<style lang="stylus" scoped>
.helper
font-weight: 100
display: flex
justify-content: space-between
padding: 5px 0
line-height: 30px
background-color: #ffffff
font-size: 14px
font-smoothing: antialiased
.left, .clear, .tabs
padding: 0 10px
box-sizing: border-box
font-weight: bold
.left, .clear
width: 150px
.left
text-align: left
.clear
text-align: right
cursor: pointer
.tabs
width: 200px
display: flex
justify-content: space-around
*
display: inline-block
padding: 0 10px
cursor: pointer
border: 1px solid rgba(175, 47, 47, 0)
&.actived
border-color: rgba(175, 47, 47, 0.4)
border-radius: 5px
</style>

todo.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<template>
<section class="real-app">
<input
type="text"
class="add-input"
autofocus="autofous"
@keyup.enter="addTodo"
placeholder="接下去要做什么?"
/>
<Item
:todo="todo"
v-for="todo in filteredTodos"
:key="todo.id"
@del="deleteTodo"
/>
<Tabs
:filter="filter"
:todos="todos"
@clearAllCompleted="clearAllCompleted"
@toggleFilter="toggleFilter"
/>
</section>
</template>

<script>
import Item from "./item.vue";
import Tabs from "./tabs.vue";
let id = 0;
export default {
data() {
return {
todos: [],
filter: "all",
};
},
components: {
Item,
Tabs,
},
computed: {
filteredTodos() {
if (this.filter === "all") {
return this.todos;
}
const completed = this.filter === "completed";
return this.todos.filter((todo) => completed === todo.completed);
},
},
methods: {
addTodo(e) {
if (e.target.value.trim() === "") {
return;
}
this.todos.unshift({
id: id++,
content: e.target.value.trim(),
completed: false,
});
e.target.value = "";
},
deleteTodo(id) {
this.todos.splice(
this.todos.findIndex((todo) => todo.id === id),
1
);
},
clearAllCompleted() {
this.todos = this.todos.filter((todo) => !todo.completed);
},
toggleFilter(state) {
this.filter = state;
},
},
};
</script>

<style lang="stylus" scoped>
.real-app
width: 700px
margin: 0 auto
box-shadow: 0 0 5px #666
.add-input
position: relative
margin: 0
width: 100%
font-size: 24px
font-family: inherit
font-weight: inherit
line-height: 1.4em
border: 0
outline: none
color: inherit
box-sizing: border-box
font-smoothing: antialiased
padding: 16px 16px 16px 36px
border: none
box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03)
</style>

修改 app.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<template>
<div id="app">
<div id="cover"></div>
<Header></Header>
<todo></todo>
<Footer></Footer>
</div>
</template>

<script>
import Header from "./todo/header.vue";
import Footer from "./todo/footer.jsx";
import Todo from "./todo/todo.vue";
export default {
components: {
Header,
Footer,
Todo,
},
};
</script>

<!-- 设置scoped 表示当前组件下的id只在当前组件起作用,不会跟其他组件引起冲突 -->
<style lang="stylus" scoped>
#app
position: absolute
left: 0
right: 0
top: 0
bottom: 0
#cover
position: absolute
left: 0
right: 0
top: 0
bottom: 0
background-color: #555
opacity: 0.5
z-index: -1
</style>

优化打包

1
2
3
4
5
6
7
npm install extract-text-webpack-plugin

npm uninstall extract-text-webpack-plugin

extract-text-webpack-plugin 默认版本只支持到webpack3.0

npm install mini-css-extract-plugin

修改 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const path = require("path");
const HTMLPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const isDev = process.env.NODE_NEV === "development";

const config = {
target: "web",
entry: path.join(__dirname, "src/index.js"),
output: {
filename: "bundle.[hash:8].js",
path: path.join(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.jsx$/,
loader: "babel-loader",
},
// {
// test: /\.css$/,
// use: ['style-loader', 'css-loader']
// },
// {
// test: /\.styl/,
// use: [
// 'style-loader',
// 'css-loader',
// {
// loader: 'postcss-loader',
// options: {
// sourceMap: true,
// }
// },
// 'stylus-loader'
// ]
// },
{
test: /\.(gif|jpg|jpeg|png|svg)$/,
use: [
{
loader: "url-loader",
options: {
limit: 1024,
name: "[name].[ext]",
},
},
],
},
],
},
plugins: [
new webpack.DefinePlugin({
"process.env": {
NODE_NEV: isDev ? '"development"' : '"production"',
},
}),
new VueLoaderPlugin(),
new HTMLPlugin(),
],
};

if (isDev) {
config.module.rules.push({
test: /\.styl/,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
sourceMap: true,
},
},
"stylus-loader",
],
});
config.devtool = "#cheap-module-eval-source-map";
config.devServer = {
post: "8000",
host: "0.0.0.0",
overlay: {
errors: true,
},
hot: true,
};
config.plugins.push(new webpack.HotModuleReplacementPlugin());
} else {
config.entry = {
app: path.join(__dirname, "src/index.js"),
vendor: ["vue"],
};
config.output.filename = "[name].[chunkhash:8].js";
config.module.rules.push({
test: /\.styl/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
{
loader: "postcss-loader",
options: {
sourceMap: true,
},
},
"stylus-loader",
],
});
config.plugins.push(
new MiniCssExtractPlugin({
filename: "css/[name].[chunkhash:8].css",
chunkFilename: "css/[id].[chunkhash:8].css",
})
// 将类库文件单独打包出来
// new webpack.optimize.CommonsChunkPlugin({
// name: 'vendor'
// })
);
config.optimization = {
splitChunks: {
cacheGroups: {
// 这里开始设置缓存的 chunks
commons: {
chunks: "initial", // 必须三选一: "initial" | "all" | "async"(默认就是异步)
minSize: 0, // 最小尺寸,默认0,
minChunks: 2, // 最小 chunk ,默认1
maxInitialRequests: 5, // 最大初始化请求书,默认1
},
vendor: {
test: /node_modules/, // 正则规则验证,如果符合就提取 chunk
chunks: "initial", // 必须三选一: "initial" | "all" | "async"(默认就是异步)
name: "vendor", // 要缓存的 分隔出来的 chunk 名称
priority: 10, // 缓存组优先级
enforce: true,
},
},
},
runtimeChunk: true,
};
}

module.exports = config;

Webpack4 更新(创建新文件夹及文件)

源文件目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
│  .babelrc
│ .gitignore
│ package-lock.json
│ package.json
│ postcss.config.js
│ webpack.config.js

└─src
│ app.vue
│ index.js

├─assets
│ ├─images
│ │ 8.jpg
│ │ 92.png
│ │ logo.png
│ │
│ └─styles
│ footer.styl
test-stylus.styl
│ test.css

└─todo
footer.jsx
header.vue
item.vue
tabs.vue
todo.vue

现在目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
│  .babelrc
│ .gitignore
│ package-lock.json
│ package.json
│ postcss.config.js
│ webpack.config.js

├─build
│ vue-loader.config.js
│ webpack.config.base.js
│ webpack.config.client.js

└─src
│ app.vue
│ index.js

├─assets
│ ├─images
│ │ 8.jpg
│ │ 92.png
│ │ logo.png
│ │
│ └─styles
│ footer.styl
test-stylus.styl
│ test.css

├─layout
│ footer.jsx
│ header.vue

└─views
└─todo
item.vue
tabs.vue
todo.vue

Vue 的更改只有 url 的引用位置发生了变化主要更改为 webpack.config,**.js 和 package.json

安装包

1
npm i webpack-merge -D

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
{
"name": "vuedemo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build:client": "cross-env NODE_ENV=production webpack --config build/webpack.config.client.js",
"dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.client.js"
},
"repository": {
"type": "git",
"url": "http://120.27.240.127:8099/root/vueDemo.git"
},
"author": "",
"license": "ISC",
"dependencies": {
"vue": "^2.6.12"
},
"devDependencies": {
"autoprefixer": "^10.0.1",
"babel-core": "^6.26.3",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.5",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-vue-jsx": "^3.7.0",
"babel-preset-env": "^1.7.0",
"cross-env": "^7.0.2",
"css-loader": "^5.0.1",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^4.5.0",
"mini-css-extract-plugin": "^1.3.0",
"postcss": "^8.1.6",
"postcss-loader": "^4.0.4",
"rimraf": "^3.0.2",
"style-loader": "^2.0.0",
"stylus": "^0.54.8",
"stylus-loader": "^4.2.0",
"url-loader": "^4.1.1",
"vue-loader": "^15.9.5",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.12",
"webpack": "^4.27.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^5.3.0"
}
}

webpack.config.base.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const path = require("path");

const isDev = process.env.NODE_NEV === "development";

const config = {
target: "web",
entry: path.join(__dirname, "../src/index.js"),
output: {
filename: "bundle.[hash:8].js",
path: path.join(__dirname, "../dist"),
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.jsx$/,
loader: "babel-loader",
},
{
test: /\.js$/,
loader: "babel-loader",
exclude: /node_modules/,
},
{
test: /\.(gif|jpg|jpeg|png|svg)$/,
use: [
{
loader: "url-loader",
options: {
limit: 1024,
name: "resources/[path][name].[hash:8].[ext]",
publicPath: "/",
},
},
],
},
],
},
};

module.exports = config;

webpack.config.cliemt.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const path = require("path");
const HTMLPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
const { merge } = require("webpack-merge");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const baseConfig = require("./webpack.config.base");

const isDev = process.env.NODE_NEV === "development";

const devServer = {
post: "8000",
host: "0.0.0.0",
overlay: {
errors: true,
},
hot: true,
};

const defaltPlugins = [
new webpack.DefinePlugin({
"process.env": {
NODE_NEV: isDev ? '"development"' : '"production"',
},
}),
new VueLoaderPlugin(),
new HTMLPlugin(),
];

let config;

if (isDev) {
config = merge(baseConfig, {
devtool: "#cheap-module-eval-source-map",
module: {
rules: [
{
test: /\.styl/,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
sourceMap: true,
},
},
"stylus-loader",
],
},
],
},
devServer,
plugins: defaltPlugins.concat([new webpack.HotModuleReplacementPlugin()]),
});
} else {
config = merge(baseConfig, {
entry: {
app: path.join(__dirname, "../src/index.js"),
vendor: ["vue"],
},
output: {
filename: "[name].[chunkhash:8].js",
},
module: {
rules: [
{
test: /\.styl/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
{
loader: "postcss-loader",
options: {
sourceMap: true,
},
},
"stylus-loader",
],
},
],
},
plugins: defaltPlugins.concat([
new MiniCssExtractPlugin({
filename: "css/[name].[chunkhash:8].css",
chunkFilename: "css/[id].[chunkhash:8].css",
}),
]),
optimization: {
splitChunks: {
cacheGroups: {
// 这里开始设置缓存的 chunks
commons: {
chunks: "initial", // 必须三选一: "initial" | "all" | "async"(默认就是异步)
minSize: 0, // 最小尺寸,默认0,
minChunks: 2, // 最小 chunk ,默认1
maxInitialRequests: 5, // 最大初始化请求书,默认1
},
vendor: {
test: /node_modules/, // 正则规则验证,如果符合就提取 chunk
chunks: "initial", // 必须三选一: "initial" | "all" | "async"(默认就是异步)
name: "vendor", // 要缓存的 分隔出来的 chunk 名称
priority: 10, // 缓存组优先级
enforce: true,
},
},
},
runtimeChunk: true,
},
});
}

module.exports = config;

vue 中的 css 热加载

1
npm i vue-style-loader

修改 webpack.config.cliemt.js

1
style-loader > vue-style-loader

重新打包时自动去除上次打包

安装包

1
npm i rimraf -D

修改 package.json

1
2
3
4
5
6
7
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build:client": "cross-env NODE_ENV=production webpack --config build/webpack.config.client.js",
"build": "npm run clean && npm run build:client",
"clean": "rimraf dist",
"dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.client.js"
}

添加 vue-loader.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//const docsLoader = require.resolve('./doc-loader')

module.exports = (isDev) => {
return {
//去除空格
preserveWhitepace: true,
//Vue中的CSS进行提取到CSS
extractCss: !isDev,
cssModules: {
//命名
localIndentName: isDev
? "[path][name]-[hash:base64:5]"
: "[hash:base64:5]",
//-转大写
camelCase: true,
},
//热重载(根据环境变量生成)
//hotReload: true
//自定义loader
// loaders: {
// 'docs': docsLoader,
// js: 'coffe-loader'
// },
// //优先解析Loader
// preLoader: {

// },
// //后手解析
// postLoader: {

// }
};
};

修改 webpack.config.client.js

1
2
3
4
5
{
test: /\.vue$/,
loader: 'vue-loader',
options: createVueLoaderOptions(isDev)
}

eslint 校验

1
2
3
4
5
6
7
8
9
10
11
12
npm i eslint eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node -D

npm i acorn -D

vue中的html
npm i eslint-plugin-html -D

npm i eslint-plugin-vue -D

npm i eslint-loader babel-eslint -D

太多 太多问题 暂时不写出来

git 提交时进行校验

1
npm i husky -D

在 package.json 的 scripts 添加

1
"precommit": "npm run lint-fix",