简朴vue项目脚手架

简朴vue项目脚手架

github地点

运用手艺栈

  • webpack(^2.6.1)

  • webpack-dev-server(^2.4.5)

  • vue(^2.3.3)

  • vuex(^2.3.1)

  • vue-router(^2.5.3)

  • vue-loader(^12.2.1)

  • eslint(^3.19.0)

须要进修的学问

vue.js
vuex
vue-router
vue-loader
webpack2
eslint
内容相称多,尤其是webpack2教程,官方脚手架vue-cli虽然相称完全完全,然则修改起来照样挺花时间,因而本身参照网上的材料和之前做过的项目用到的构建东西地去写了一个简朴vue项目脚手架。适用于多页面spa情势的营业场景(每一个模块都是一个spa)。比较简朴,重要就是一个webpack.config.js文件,没有说特地地去分别身分webpack.dev.config.js、webpack.prov.config.js等等。下面是全部webpack.config.js文件代码:

const { resolve } = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const glob = require('glob')

module.exports = (options = {}) => {
    // 设置文件,依据 run script差别的config参数来挪用差别config
    const config = require('./config/' + (process.env.npm_config_config || options.config || 'dev'))
    // 遍历进口文件,这里进口文件与模板文件名字保持一致,保证能同时合成HtmlWebpackPlugin数组和进口文件数组
    const entries = glob.sync('./src/modules/*.js')
    const entryJsList = {}
    const entryHtmlList = []
    for (const path of entries) {
        const chunkName = path.slice('./src/modules/'.length, -'.js'.length)
        entryJsList[chunkName] = path
        entryHtmlList.push(new HtmlWebpackPlugin({
            template: path.replace('.js', '.html'),
            filename: 'modules/' + chunkName + '.html',
            chunks: ['manifest', 'vendor', chunkName]
        }))
    }
    // 处置惩罚开辟环境和临盆环境ExtractTextPlugin的运用情况
    function cssLoaders(loader, opt) {
        const loaders = loader.split('!')
        const opts = opt || {}
        if (options.dev) {
            if (opts.extract) {
                return loader
            } else {
                return loaders
            }
        } else {
            const fallbackLoader = loaders.shift()
            return ExtractTextPlugin.extract({
                use: loaders,
                fallback: fallbackLoader
            })
        }
    }

    const webpackObj = {
        entry: Object.assign({
            vendor: ['vue', 'vuex', 'vue-router']
        }, entryJsList),
        // 文件内容天生哈希值chunkhash,运用hash会更新一切文件
        output: {
            path: resolve(__dirname, 'dist'),
            filename: options.dev ? 'static/js/[name].js' : 'static/js/[name].[chunkhash].js',
            chunkFilename: 'static/js/[id].[chunkhash].js',
            publicPath: config.publicPath
        },

        externals: {

        },

        module: {
            rules: [
                // 只 lint 当地 *.vue 文件,须要装置eslint-plugin-html,并设置eslintConfig(package.json)
                {
                    enforce: 'pre',
                    test: /.vue$/,
                    loader: 'eslint-loader',
                    exclude: /node_modules/
                },
                /*
                    http://blog.guowenfh.com/2016/08/07/ESLint-Rules/
                    http://eslint.cn/docs/user-guide/configuring
                    [eslint材料]
                 */
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    use: ['babel-loader', 'eslint-loader']
                },
                // 须要装置vue-template-compiler,不然编译报错
                {
                    test: /\.vue$/,
                    loader: 'vue-loader',
                    options: {
                        loaders: {
                            sass: cssLoaders('vue-style-loader!css-loader!sass-loader', { extract: true })
                        }
                    }
                },
                {
                    // 须要有响应的css-loader,因为第三方库能够会有文件
                    // (如:element-ui) css在node_moudle
                    // 临盆环境才须要code抽离,不然的话,会使热重载失效
                    test: /\.css$/,
                    use: cssLoaders('style-loader!css-loader')
                },
                {
                    test: /\.(scss|sass)$/,
                    use: cssLoaders('style-loader!css-loader!sass-loader')
                },
                {
                    test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
                    use: [
                        {
                            loader: 'url-loader',
                            options: {
                                limit: 10000,
                                name: 'static/imgs/[name].[ext]?[hash]'
                            }
                        }
                    ]
                }
            ]
        },

        plugins: [
            ...entryHtmlList,
            // 抽离css
            new ExtractTextPlugin({
                filename: 'static/css/[name].[chunkhash].css',
                allChunks: true
            }),
            // 抽离大众代码
            new webpack.optimize.CommonsChunkPlugin({
                names: ['vendor', 'manifest']
            }),
            // 定义全局常量
            // cli敕令行运用process.env.NODE_ENV不如希冀效果,运用不了,所以须要运用DefinePlugin插件定义,定义情势'"development"'或JSON.stringify('development')
            new webpack.DefinePlugin({
                'process.env': {
                    NODE_ENV: options.dev ? JSON.stringify('development') : JSON.stringify('production')
                }
            })

        ],

        resolve: {
            // require时省略的扩展名,不再须要强迫转入一个空字符串,如:require('module') 不须要module.js
            extensions: ['.js', '.json', '.vue', '.scss', '.css'],
            // require途径简化
            alias: {
                '~': resolve(__dirname, 'src'),
                // Vue 最早会打包天生三个文件,一个是 runtime only 的文件 vue.common.js,一个是 compiler only 的文件 compiler.js,一个是 runtime + compiler 的文件 vue.js。
                // vue.js = vue.common.js + compiler.js,默许package.json的main是指向vue.common.js,而template 属性的运用肯定要用compiler.js,因而须要在alias转变vue指向
                vue: 'vue/dist/vue'
            },
            // 指定import从哪一个目次开始查找
            modules: [
                resolve(__dirname, 'src'),
                'node_modules'
            ]
        },
        // 开启http效劳,publicPath => 须要与Output保持一致 || proxy => 反向代办 || port => 端口号
        devServer: config.devServer ? {
            port: config.devServer.port,
            proxy: config.devServer.proxy,
            publicPath: config.publicPath,
            stats: { colors: true }
        } : undefined,
        // 屏障文件凌驾限定大小的warn
        performance: {
            hints: options.dev ? false : 'warning'
        },
        // 天生devtool,保证在浏览器能够看到源代码,临盆环境设为false
        devtool: 'inline-source-map'
    }

    if (!options.dev) {
        webpackObj.devtool = false
        webpackObj.plugins = (webpackObj.plugins || []).concat([
            // 紧缩js
            new webpack.optimize.UglifyJsPlugin({
                // webpack2,默许为true,能够不必设置
                compress: {
                    warnings: false
                }
            }),
            //  紧缩 loaders
            new webpack.LoaderOptionsPlugin({
                minimize: true
            })
        ])
    }

    return webpackObj
}

上面的代码关于每一个设置项都有解释申明,这里有几点须要注重的:

1. webpack.config.js导出的是一个function

之前项目的webpack.config.js是以对象情势export的,以下

module.exports = {
    entry: ...,
    output: {
        ...
    },
    ...
}

而如今倒出来的是一个function,以下:

module.exports = (options = {}) => { 
    return {
        entry: ...,
        output: {
            ...
        },
        ...
    }
}

如许的话,function会在实行webpack CLI的时刻猎取webpack的参数,经由历程options传进function,看一下package.json:

    "local": "npm run dev --config=local",
    "dev": "webpack-dev-server -d --hot --inline --env.dev --env.config dev",
    "build": "rimraf dist && webpack -p --env.config prod" //rimraf清空dist目次

关于local敕令,我们实行的是dev敕令,然则在末了面会--config=local,这是设置,如许我们能够经由历程process.env.npm_config_config猎取到,而关于dev敕令,关于--env XXX,我们便能够在function猎取option.config= ‘dev’ 和 option.dev= true的值,迥殊轻易!以此便能够同步参数来加载差别的设置文件了。关于-d-p不清晰的话,能够这里检察,很细致!

    // 设置文件,依据 run script差别的config参数来挪用差别config
    const config = require('./config/' + (process.env.npm_config_config || options.config || 'dev'))

2. modules安排模板文件、进口文件、对应模块的vue文件

将进口文件和模板文件放到modules目次(名字保持一致),webpack文件会经由历程glob读取modules目次,遍历天生进口文件对象和模板文件数组,以下:

    const entries = glob.sync('./src/modules/*.js')
    const entryJsList = {}
    const entryHtmlList = []
    for (const path of entries) {
        const chunkName = path.slice('./src/modules/'.length, -'.js'.length)
        entryJsList[chunkName] = path
        entryHtmlList.push(new HtmlWebpackPlugin({
            template: path.replace('.js', '.html'),
            filename: 'modules/' + chunkName + '.html',
            chunks: ['manifest', 'vendor', chunkName]
        }))
    }

关于HtmlWebpackPlugin插件中几个设置项的意义是,template:模板途径,filename:文件称号,这里为了辨别开来模板文件我是安排在dist/modules文件夹中,而对应的编译打包好的js、img(关于图片我们是运用file-loader、url-loader举行抽离,关于这两个不是很明白的,能够看这里)、css我也是会放在dist/下对应目次的,如许目次会比较清晰。chunks:指定插进去文件中的chunk,背面我们会天生manifest文件、大众vendor、以及对应天生的jscss(称号一样)

3. 处置惩罚开辟环境和临盆环境ExtractTextPlugin的运用情况

开辟环境,不须要把css举行抽离,要以style插进去html文件中,能够很好完成热替代
临盆环境,须要把css举行抽离兼并,以下(依据options.dev辨别开辟和临盆):

    // 处置惩罚开辟环境和临盆环境ExtractTextPlugin的运用情况
    function cssLoaders(loader, opt) {
        const loaders = loader.split('!')
        const opts = opt || {}
        if (options.dev) {
            if (opts.extract) {
                return loader
            } else {
                return loaders
            }
        } else {
            const fallbackLoader = loaders.shift()
            return ExtractTextPlugin.extract({
                use: loaders,
                fallback: fallbackLoader
            })
        }
    }
    ...
    // 运用情况
    // 注重:须要装置vue-template-compiler,不然编译会报错
    {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
            loaders: {
                sass: cssLoaders('vue-style-loader!css-loader!sass-loader', { extract: true })
            }
        }
    },
    ...
    {
        test: /\.(scss|sass)$/,
        use: cssLoaders('style-loader!css-loader!sass-loader')
    }

再运用ExtractTextPlugin兼并抽离到static/css/目次

4. 定义全局常量

cli敕令行(webpack -p)运用process.env.NODE_ENV不如希冀效果,运用不了,所以须要运用DefinePlugin插件定义,定义情势'”development”‘或JSON.stringify(process.env.NODE_ENV),我运用如许的写法’development’,效果报错(针对webpack2),查找了一下网上材料,是如许讲的,能够去看一下,设置以下:

    new webpack.DefinePlugin({
        'process.env': {
            NODE_ENV: options.dev ? JSON.stringify('development') : JSON.stringify('production')
        }
    })

5. 运用eslint修改代码范例

经由历程eslint来搜检代码的范例性,经由历程定义一套设置项,来范例代码,如许多人合作,写出来的代码也会比较文雅,不好的处所是,就是设置项太多,有些默许项设置我们不须要,然则确是到处限定我们,须要经由历程设置屏障掉,能够经由历程.eslintrc 文件或是package.json的eslintConfig,另有其他体式格局,能够到中文网看,这里我用的是package.json体式格局,以下:

    ...
  "eslintConfig": {
    "parser": "babel-eslint",
    "extends": "enough",
    "env": {
      "browser": true,
      "node": true,
      "commonjs": true,
      "es6": true
    },
    "rules": {
      "linebreak-style": 0,
      "indent": [2, 4],
      "no-unused-vars": 0,
      "no-console": 0
    },
    "plugins": [
      "html"
    ]
  },
  ...

我们还须要装置 npm install eslint eslint-config-enough eslint-loader --save-dev,eslint-config-enough是所谓的设置文件,如许package.json的内容才起效,然则不当当是如许,对应编辑器也须要装置对应的插件,sublime text 3须要装置SublimeLinter、SublimeLinter-contrib-eslint插件。关于一切划定规矩的详解,能够去看官网,也能够去这里看,很细致!
因为我们运用的是vue-loader,天然我们是愿望能对.vue文件eslint,那末须要装置eslint-plugin-html,在package.json中举行设置。然后对应webpack设置:

    {
        enforce: 'pre',
        test: /.vue$/,
        loader: 'eslint-loader',
        exclude: /node_modules/
    }

我们会发明webpack v1和v2之间会有一些差别,比方webpack1关于预先加载器处置惩罚的实行是如许的,

  module: {
    preLoaders: [
      {
        test: /\.js$/,
        loader: "eslint-loader"
      }
    ]
  }

更多的差别能够到中文网看,很细致,不做拓展。

6. alias vue指向题目

    ...
    alias: {
        vue: 'vue/dist/vue'
    },
    ...

Vue 最早会打包天生三个文件,一个是 runtime only 的文件 vue.common.js,一个是 compiler only 的文件 compiler.js,一个是 runtime + compiler 的文件 vue.js。
vue.js = vue.common.js + compiler.js,默许package.json的main是指向vue.common.js,而template 属性的运用肯定要用compiler.js,因而须要在alias转变vue指向

7. devServer的运用

之前的项目中运用的是用express启动http效劳,webpack-dev-middleware+webpack-hot-middleware,这里会用到compiler+compilation,这个是webpack的编译器和编译历程的一些学问,也不是很懂,后续要去做做作业,应当能够加深对webpack运行机制的明白。如许做的话,觉得庞杂许多,关于webpack2.0 devServer好像功用更壮大越发完美了,所以直接运用就能够了。以下:

    devServer: {
        port: 8080, //端口号
        proxy: { //方向代办 /api/auth/ => http://api.example.dev
            '/api/auth/': {
                target: 'http://api.example.dev',
                changeOrigin: true,
                pathRewrite: { '^/api': '' }
            }
        },
        publicPath: config.publicPath,
        stats: { colors: true }
    }
    //changeOrigin会修改HTTP要求头中的Host为target的域名, 这里会被改成api.example.dev
    //pathRewrite用来改写URL, 这里我们把/api前缀去掉,直接运用/auth/要求

webpack 2 打包实战解说得异常好,异常棒。能够去看一下,肯定会有所收成!

8. 热重载道理

webpack中文网,讲的还算清晰,不过能够太笨,看起来照样云里雾里的,似懂非懂的,补补课,好好看看。

9. localtunnel的运用

Localtunnel 是一个能够让内网效劳器暴露到公网上的开源项目,运用能够看这里

$ npm install -g localtunnel
$ lt --port 8080
your url is: https://uhhzexcifv.localtunnel.me

如许的话,能够把我们的当地网站临时性地暴露到公网,能够对网站做一些线上线下对照,细致内容能够去相识一下localtunnel,这里讲的是经由历程上面设置,接见https://uhhzexcifv.localtunnel.me,没有到达抱负效果,涌现了Invalid Host header的毛病,因为devServer缺乏一个设置disableHostCheck: true,如许的一个设置,许多文档上面都没有申明,字面上面的意义不要去搜检Host,如许设置,便能够绕过这一层磨练,设置的设置项在optionsSchema.json中,issue能够看这里

    原文作者:xiaobinwu
    原文地址: https://segmentfault.com/a/1190000009712172
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞