webpack4+vue2手动搭建前端开发框架

2,459 阅读3分钟

1.前言

之前开发一直是在公司搭好的框架里面,或者是vue@cli脚手架的开箱即用,项目在遇到问题后排查也会花大量时间,所以索性自己研究手动搭建一个,也熟悉一下里面的原理。 搭建这个是在windows环境下,mac大同小异,实现的基本功能有:

  • 支持热更新
  • 支持vue全家桶+scss开发
  • 编译后文件命名规则
js/[name]-[hash:8].js    
css/[name]-[hash:8].css
  • 支持生产环境的图片压缩
  • 支持打包后的压缩JS代码
  • 支持dist文件自动压缩

2.安装node

node官网直接下载最新版,下载完成按默认路径安装。安装成功后在cmd命令框输入 node -v查看是否成。

3.项目初始化

cmd命令框执行npm init(也可以执行npm init -y,会跳过一些默认项),执行成功后会在当前文件夹默认创建package.json文件

4.安装webpack

$ npm install webpack --save-de

安装成功后默认会有node_modules、package-lock.json文件出现

5.安装vue全家桶及所需插件

因为这些插件都是通过npm逐一安装的,这里就直接贴出package.json, 直接执行npm install安装即可,另外因网络而异,有时npm安装会失败,遇到这种情况不要用该文件的内容,先安装cnpm,然后把需要插件的相关内容加到package.json文件,cnpm install即可。 参数说明

{
  "name": "henry-test",
  "version": "1.0.0",
  "description": "test-project",
  "main": "src/main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --inline --progress --config webpack.base.config.js",
    "build": "webpack -p --config webpack.prod.config.js"
  },
  "keywords": [],
  "author": "henry",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.19.2",
    "vue": "^2.6.11",
    "vue-router": "^3.2.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@babel/core": "^7.9.6",
    "babel-core": "^6.26.3",
    "babel-loader": "^8.1.0",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-env": "^1.7.0",
    "babel-preset-stage-2": "^6.24.1",
    "chalk": "^4.0.0",
    "child_process": "^1.0.2",
    "clean-webpack-plugin": "^3.0.0",
    "cnpm": "^6.1.1",
    "css-loader": "^3.5.3",
    "express": "^4.17.1",
    "filemanager-webpack-plugin": "^2.0.5",
    "html-webpack-plugin": "^4.3.0",
    "imagemin-webpack-plugin": "^2.4.2",
    "mini-css-extract-plugin": "^0.9.0",
    "node-sass": "^4.14.1",
    "open-browser-webpack-plugin": "0.0.5",
    "ora": "^4.0.4",
    "postcss-loader": "^3.0.0",
    "rimraf": "^3.0.2",
    "sass": "^1.26.5",
    "sass-loader": "^8.0.2",
    "semver": "^7.3.2",
    "shelljs": "^0.8.4",
    "style-loader": "^1.2.1",
    "uglifyjs-webpack-plugin": "^2.2.0",
    "url-loader": "^4.1.0",
    "vue-loader": "^15.5.1",
    "vue-style-loader": "^2.0.0",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.1.2",
    "webpack-dev-middleware": "^3.7.2",
    "webpack-dev-server": "^3.11.0",
    "webpack-hot-middleware": "^2.25.0",
    "webpack-merge": "^4.2.2",
    "webpack-parallel-uglify-plugin": "^1.1.2"
  },
  "engines": {
    "node": ">= 4.0.0",
    "npm": ">= 3.0.0"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

6.创建文件目录结构

目录结构参考地址

index.html

<!DOCTYPE html>
<html lang="en">
<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">
    <link rel="icon" href="/favicon.ico">
    <title>手动搭建前端开发框架</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>

main.js

import Vue from 'vue'
import App from './app'
import router from './router/index'
import './static/css/common.scss';
//实例化vue
new Vue({
    router,// 路由
    render:h=>h(App)
}).$mount('#app');

app.vue

<template>
    <div id="app">
        <div>{{name}}</div>
        <router-view></router-view>
    </div>
</template>

<script>
    export default {
        name: "app",
        data(){
            return{
                name:'VUE架构'
            }
        },
        mounted() {
        }
    }
</script>

<style scoped lang="scss">

</style>

router/common.js

const routes = [
    {
        path: '/',
        name: 'home',
        component:() => import('@/page/home.vue'),
    },
    {
        path: '/other',
        name: 'other',
        component: () => import('@/page/other.vue'),
    },
]
export default routes

router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './common'
Vue.use(VueRouter);
const router = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes
})
export default router;

webpack.base.config.js

const path = require('path');
const htmlPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
// const OpenBrowserPlugin = require('open-browser-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
const ImageminPlugin = require('imagemin-webpack-plugin').default ;//引用方式注意
const webpack = require('webpack')
module.exports = {
    mode: process.env.NODE_ENV,
    // 入口文件
    entry:path.resolve(__dirname, 'src/main.js'),
    //编译输出配置
    output: {
        path:path.resolve(__dirname,'dist'), // 输出文件夹
        filename: process.env.NODE_ENV !== 'production' ? 'js/[name].js' : 'js/[name]-[hash:8].js',
        chunkFilename: process.env.NODE_ENV !== 'production' ? 'js/[name].js' : 'js/[name]-[hash:8].js',//动态import文件名
    },
    devServer:{
        port:8082,  ///端口
        contentBase: path.join(__dirname, 'dist'),
        inline:true,
        open:true
    },
    resolve:{
        extensions: ['.js', '.vue'],  //js 和 vue 文件在import导入的时候不需要带扩展
        alias: {
            'vue$': 'vue/dist/vue.esm.js',  //vue官方指定写法,如果不写这个,则运行的时候会提示
            '@': path.resolve(__dirname, 'src')  //给src目录起个别名@ ,引用src目录的时候,可用@替代
        }
    },
    // 下面是loader的配置
    module:{
        rules: [
            {
                test: /\.js/,
                use: ['babel-loader'],
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    loaders: {
                        scss: 'vue-style-loader!css-loader!sass-loader!style-loader', //
                    }
                }
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    'style-loader',//会在头部导入style标签,只针对.css文件
                    'css-loader'
                ],
            },
            {
                test: /\.(sa|sc|c)ss$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    'css-loader',
                    'sass-loader',
                ],
            },
            {
                test: /(\.jpg|\.png|\.jpeg|\.gif)$/i,
                loader: 'url-loader',
                options:{
                    limit: 1024,
                    name: '[name]-[hash:7].[ext]'
                }
            }
        ]
    },
    // 插件的配置
    plugins:[
        new htmlPlugin({
            title: 'vue',
            filename:'index.html',//源文件名
            template:'./index.html',//指定打包压缩的文件
            minify:{
                removeComments:true,//清除注释
                collapseWhitespace:true//清理空格
            },
            hash: true
        }),
        new VueLoaderPlugin(),
        new CleanWebpackPlugin(),//清理构建文件夹
        new webpack.HotModuleReplacementPlugin(),
        new ImageminPlugin({
            test: /\.(jpe?g|png|gif|svg)$/i,
            disable: process.env.NODE_ENV !== 'production', // Disable during development
            pngquant: {
                quality: '95-100'
            }
        }),
        // 使用 ParallelUglifyPlugin 并行压缩输出JS代码
        new ParallelUglifyPlugin({
            // 传递给 UglifyJS的参数如下:
            test: /.js$/g,
            /*
            uglifyJS:用于压缩 ES5 代码时的配置,Object 类型,直接透传给 UglifyJS 的参数。
            uglifyES:用于压缩 ES6 代码时的配置,Object 类型,直接透传给 UglifyES 的参数。
            */
            uglifyES: {
                    output: {
                        /*
                         是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,
                         可以设置为false
                        */
                    beautify: false,
                    /*
                     是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false
                    */
                    comments: false
                },
                compress: {
                    /*
                     是否在UglifyJS删除没有用到的代码时输出警告信息,默认为输出,可以设置为false关闭这些作用
                     不大的警告
                    */
                    warnings: false,
                    /*
                     是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句
                    */
                    drop_console: true,
                    /*
                     是否内嵌虽然已经定义了,但是只用到一次的变量,比如将 var x = 1; y = x, 转换成 y = 5, 默认为不
                     转换,为了达到更好的压缩效果,可以设置为false
                    */
                    collapse_vars: true,
                    /*
                     是否提取出现了多次但是没有定义成变量去引用的静态值,比如将 x = 'xxx'; y = 'xxx'  转换成
                     var a = 'xxxx'; x = a; y = a; 默认为不转换,为了达到更好的压缩效果,可以设置为false
                    */
                    reduce_vars: true
                }
            }
        }),
        new MiniCssExtractPlugin({
            filename: process.env.NODE_ENV !== 'production' ? 'css/[name].css' : 'css/[name]-[hash:8].css',
            chunkFilename:  process.env.NODE_ENV !== 'production' ? 'css/[name].css' : 'css/[name]-[hash:8].css',
        })
    ]
}

webpack.prod.config.js

const webpackBase = require('./webpack.base.config');
const webpackMerge = require('webpack-merge');
const FileManagerPlugin = require('filemanager-webpack-plugin');
module.exports = webpackMerge(webpackBase, {
    plugins:[
        new FileManagerPlugin({  //初始化 filemanager-webpack-plugin 插件实例
            onEnd: {
                delete: [   //首先需要删除项目根目录下的dist.zip
                    './architecture-demo.zip',
                ],
                archive: [ //然后我们选择dist文件夹将之打包成architecture-demo.zip并放在根目录
                    {source: './dist', destination: './architecture-demo.zip'},
                ]
            }
        })
    ]
});

7.写在最后

到这里一个简单的框架已经搭完了,是不是感觉很简单呢!附上例子地址 github.com/tinzai/arch…