用了好几次vue-cli,还是决定手工搭建一个vue2+webpack4项目
1.为了可拓展性
2.vue.config.js真是用的不开心
3.对webpack4和vue2的合作了解的更多
本文代码的git地址 github.com/shaohuanhua…
参考的 www.jianshu.com/p/6ff34032a…
前面几步基本一样,为了更易理解,文件命名和位置做了改动,更接近vue-cli3
一,初级构建
新建一个imitateCli文件夹,打开终端开始构建
npm init
安装框架所需基本:
npm i webpack vue vue-loader -D
(vue-loader:解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的 Loader 去处理)
npm i webpack-cli -s -d (webpack4 已经开始使用webpack-cli)<br>
npm i css-loader vue-template-compiler -D<br>
(css-loader:加载由 vue-loader 提取出的 CSS 代码。 )
(vue-template-compiler:把 vue-loader 提取出的 HTML 模版编译成对应的可执行的 JavaScript 代码)
然后我们在src目录下面新建一个app.vue文件,里面就可以写一些关于项目的业务代码:
<template>
<div> luckfine </div>
</template>
<script>
export default {
data () {
//text: 'luckfine'
}
}
</script>
<style>
</style>
后缀为.vue 文件是不可以在浏览器里直接运行的,我们得让它运行起来。
现在我们要在项目根目录下新建一个webpack.config.js文件,webpack(号称打包一切)它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。
我们在src目录下新建一个main.js作为入口文件,顺便在里面写点东西:
// main.js
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)
main.js准备完毕之后,那么在webpack.config.js里面就可以这样写:
// webpack.config.js
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: path.join(__dirname, 'src/main.js'),
mode:'develop',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
module: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader'
}
]
},
plugins:[
new VueLoaderPlugin()
],
}
webpack把所有的文件打包成一个bundle.js文件,并且是能在浏览器里面直接运行的代码。
现在我们可以在package.json 文件里的scripts对象里面添加一个脚本:
(注意:webpack4中已经在启动时用mode指定启动环境)
"scripts": {
"build": "webpack --config webpack.config.js --mode=development"
},
添加完这段之后,我们再去terminal执行下npm run build,会发现build成功
三,启动项目
为了本地能启动我们的项目,我们可以安装一个devServer
npm install webpack-dev-server -s -d
npm install html-webpack-plugin -d -s
此时的webpack.config.js
// webpack.config.js
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
entry: path.join(__dirname, 'src/main.js'),
mode: 'develop',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
module: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader'
}
]
},
plugins:[
new VueLoaderPlugin(),
new HtmlWebpackPlugin()
],
devServer: {
historyApiFallback: {
index: `/dist/index.html`
},
host: '0.0.0.0',
port: '8014',
disableHostCheck: true
}
}
package.json中添加启动入口
"dev": "webpack-dev-server --inline --hot --mode=development",
此时我们在terminal执行npm run dev
即可看到他自动给我们创建了一个index.html,把打包好的js插入进里面,且项目已经可以运行
0.0.0.0:8014 或者 localhost:8014
devServer应该默认hot:true的吧
四:加入常用vue拓展
引入vue-router
npm install vue-router -d -s
// main.js
import VueRouter from 'vue-router'
import router from './router' // 路由单独放个文件
Vue.use(VueRouter)
...
new Vue({
router, // 全局引入
render: (h) => h(App)
}).$mount(root)
习惯用index.html做载体,可以额外放第三方插件啥的
建文件public/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>cli demo</title>
</head>
<body>
<noscript>
<strong>不支持script</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
// main.js
import Vue from 'vue'
import App from './app.vue'
import VueRouter from 'vue-router'
import router from './router'
Vue.use(VueRouter)
// const root = document.createElement('div')
// document.body.appendChild(root)
new Vue({
router,
render: (h) => h(App)
}).$mount('#app')
<!--App.vue-->
<template>
<div> <!--这个元素及其子元素会把index.html里的#app替换掉 -->
<router-view></router-view> <!--路由匹配的组件在此-->
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {}
}
}
</script>
// router.js
import Router from 'vue-router'
// import navList from '@/router/nav.config.json'
// 也可以把路由写成个json,供多处使用
const findPwd = () => import('../views/login/find_pwd.vue') // 懒加载,需要webpack里的插件dynamic-import-webpack配置动态import,以后的es几也会写入
const routes = [{
path: '/find_pwd',
component: findPwd,
name: '找回密码'
}]
export default new Router({
routes
})
这就能通过路由访问了 http://localhost:8014/#/find_pwd
五:区分开发和生产环境配置webpack
1.新建一个文件夹build,放入3个webpack配置文件代替webpack.config.js,如下
build --webpack.base.conf.js
|--webpack.dev.config.js
|--webpack.prod.config.js
在webpack.base.conf.js
公共配置,包括对.vue, .css, .scss, 图片,字体处理的loader,babel转码,css代码提取,入口文件处理
在webpack.dev.config.js
配置devServer,eslint,html模板处理,output处理
webpack.prod.config.js
打包压缩文件,splitChunks代码分割提取,html模板处理,output处理, 代码分析
(webpack插件使用和说明都写在代码)
process.env的NODE_ENV,由下图设置
代码分割前后对比
没加splitChunk
记录剩余要加的
1.style-loader 实现了HMR接口,但是这里style-loader用mini-css-extract-plugin,所以这里css热加载不行
21.加个postCss
3.加tree-shaking配置
4.改为多入口
5.index.html里的BASE_URL?
6.加入env,分环境发包的配置
7.es6语法支持babel-preset-env/@babel/preset-env(不然用拓展符这种就报错了)
www.webpackjs.com/loaders/bab… 还是官方最靠谱
9.devServer转发
10.公共css变量
11.splitChunk只是切割代码,但是我还想按需加载呀?
2.修改包描述文件的命令
"scripts": {
"build": "webpack --config build/webpack.prod.conf.js",
"dev": "webpack-dev-server --config build/webpack.dev.conf.js --inline --hot"
},
用npm run build 虽然能打包成功,但是运行时报错,说没找到Vue。
我打开依赖包里的vue,发现主入口是dist/vue.runtime.common.js(运行版vue),
如下图, 因为vue-loader+render才能用运行版(可是深入浅出vue上说vue-loader就可以用运行版了呀?),后面再优化
所以改了main.js里的引入
import Vue from '../node_modules/vue/dist/vue.js'
果然打包的静态文件也可以运行了
六:webpack具体配置
1.style热加载
style-loader是将css-loader打包好的css代码以<style> 标签的形式插入到html文件中。
// webpack.base.config.js
{
test: /\.s?css$/,
use: [{loader: miniCssExtractPlugin.loader, // 用miniCssExtractPlugin代替了style-loader,以便css提取
options: {
hmr: process.env.NODE_ENV === 'development',
reloadAll: true // if hmr does not work, this is a forceful method.
}},
// "style-loader", // 将 JS 字符串生成为 style 节点
"css-loader", // 将 CSS 转化成 CommonJS 模块
"sass-loader" // 将 Sass 编译成 CSS
]
},
loader改成miniCssExtractPlugin.loader之后,就一直不能实现css的热加载
不过开发时也不需要分离css,所以dev.config里用vue-style-loader,prod.config里用miniCssExtractPlugin.loader
module: {
rules: [
{
test: /\.s?css$/,
use: [
"vue-style-loader" , // webpack.dev.config.js
// {loader: miniCssExtractPlugin.loader}, webpack.prod.config.js
"css-loader", // 将 CSS 转化成 CommonJS 模块
"sass-loader" // 将 Sass 编译成 CSS
]
}
]
}
2.css提取
webpack.prod.config.js配置了css的分离提取,如下。
我在vue里引了reset.scss + 内联的scss样式,用以下配置之后,
css里重复的代码,给我剔除了
css也从.vue里提取出来单独打包成了.css文件,但是咋没分割呢???
const miniCssExtractPlugin = require('mini-css-extract-plugin') // css分离提取 老的ExtractTextPlugin
let webpackConfig = merge(baseWebpackConfig, {
mode: 'production',
module: {
rules: [
{
test: /\.s?css$/,
use: [
{loader: miniCssExtractPlugin.loader},
"css-loader",
"sass-loader"
]
}
]
},
plugins: [
new miniCssExtractPlugin({
filename: "css/[name][chunkhash].css",
chunkFilename: "css/[id][chunkhash].css"
})
]
})
3.加postcss
webpack对scss的loader加一个postcss-loader, 然后主目录下新建postcss.config.js 然后安装插件
npm install --save-dev postcss-loader postcss-scss autoprefixer
module: {
rules: [
{
test: /\.s?css$/,
use: [
"vue-style-loader",
"css-loader",
"postcss-loader", // 加这
"sass-loader"
]
}
]
},
// postcss.config.js
module.exports = {
parser: 'postcss-scss',
plugins: {
'autoprefixer':{}
}
}
重跑服务就能看到简单的css代码都写好hack啦
带到项目中开发时,遇到的问题和解决方案
推荐的小插件babel-plugin-transform-runtime,但是我上面用了@babel/xxx,
这个插件自然要换成@babel/plugin-transform-runtime(见webpack.dev.conf.js),
换了还是报错 this.setDynamic is not a function
原来根据我的@babel版本得特定装他个@babel/plugin-transform-runtime@7.5.5的, development环境是好的,打包的时候又坏了,说找不到这个那个的,原来还有 --save 一个@babel/runtime
我还没写完呢
本文所写的github: github.com/shaohuanhua…