阅读 1898

vue2+webpack4搭建项目

用了好几次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

加了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就可以用运行版了呀?),后面再优化

先用完整版的vue
所以改了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啦

带到项目中开发时,遇到的问题和解决方案

  • 1.npm run build 打包的时候,报错-找不到Vue,找了一下,是我webpack.prod.config.js里的这句话写错了,等会改改 // externals: { // 'vue': 'Vue' // },
  • 2.npm run build 打包的时候,报错-找不到组件,比如views/login/login,大概是动态import给搞坏了。 果然,抄npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env webpack这句安装,忘记给--save-dev了
  • 3.想写个async await,就报错regeneratorRuntime is not defined,
    推荐的小插件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…

    文章分类
    前端
    文章标签