如何打造一个令人愉悦的前端开发环境(二)

开发 开发工具
这篇文章主要讲目前火热的打包构建方式--Webpack的使用方式。其实Webpack的入门指导文章非常多,配置方式也各有各样,如果不知道Webpack是什么或者不是很清楚各项配置含义的开发者,可以先看叶大神的入门级指南--Webpack 入门指迷。

[[169799]]

前情提要

上一篇文章介绍了目前前端比较流行的各种编辑器,以及各种流行的打包方式,***给了一个Gulp的例子,这个例子还是14年的时候写的,还有一些可以优化的空间,就不讨论了,这篇文章主要讲目前火热的打包构建方式--Webpack的使用方式。

主菜--没有开胃汤

其实Webpack的入门指导文章非常多,配置方式也各有各样,这里我推荐题叶大神的入门级指南--Webpack 入门指迷,如果不知道Webpack是什么或者不是很清楚各项配置含义的开发者,可以看此文章扫扫盲。毕竟我这篇文章并不是特别基础。

一、base.js

var path = require('path'
var baseConfig = { 
    resolve: { 
        extensions: ['''.js'], 
        fallback: [path.join(__dirname, '../node_modules')], 
        alias: { 
            'src': path.resolve(__dirname, '../src'), 
            'assets': path.resolve(__dirname, '../src/assets'), 
            'components': path.resolve(__dirname, '../src/components'
        } 
    }, 
    module: { 
        loaders: [{ 
            test: /\.js$/, 
            loader: 'babel'
            exclude: /node_modules/ 
        }, { 
            test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf|otf)(\?.*)?$/, 
            loader: 'url?limit=8192&context=client&name=[path][name].[hash:7].[ext]' 
        }, 
        { 
            test: /\.css$/, 
            loader: 'style!css!autoprefixer'
        }, 
        { 
            test: /\.scss$/, 
            loader: 'style!css!autoprefixer!sass' 
        }] 
    } 
}; 
 
module.exports = baseConfig; 
  • 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.

解读下这个基本配置:

1、resolve 解析模块依赖的时候,受影响的配置项。

extensions 决定了哪些文件后缀在引用的时候可以省略点,Webpack帮助你补全名称。

fallback 当webpack在 root(默认当前文件夹,配置时要绝对路径) 和 modulesDirectories(默认当前文件夹,相对路径)配置下面找不到相关modules,去哪个文件夹下找modules

alias 这个大家应该比较熟悉,requirejs之类的都有,就是别名,帮助你快速指向文件路径,少写不少代码,而且不用关心层级关系,需要注意的是:在scss之类的css预编译中引用要加上~,以便于让loader识别是别名引用路径。

2、module 解析不同文件使用哪些loader,这个比较简单,很多文章都有,就不多说了,注意的是,这里的scss可以换成你自己的预编译器,例如:sass、less、stylus等,或者直接用postcss都行,当然还可以用一种通用方法,后面补上。

二、开发环境配置--config

var webpack = require('webpack'); 
var path = require('path'
var merge = require('webpack-merge'
var baseConfig = require('./webpack.base'
var getEntries = require('./getEntries'
 
var hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'
 
var assetsInsert = require('./assetsInsert'
 
module.exports = merge(baseConfig, { 
  entry: getEntries(hotMiddlewareScript), 
  devtool: '#eval-source-map'
  output: { 
    filename: './[name].[hash].js'
    path: path.resolve('./dist'), 
    publicPath:'./dist' 
  }, 
  plugins: [ 
    new webpack.DefinePlugin({ 
      'process.env': { 
        NODE_ENV: '"development"' 
      } 
    }), 
    new webpack.optimize.OccurenceOrderPlugin(), 
    new webpack.HotModuleReplacementPlugin(), 
    new webpack.NoErrorsPlugin(), 
    new assetsInsert() 
  ] 
}) 
  • 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.

说说这个配置中的一些难点:

1、getEntries 是用来配置入口文件,一般很多人是自己手写,或者SPA页面,只有一个入口, 很容易就写出来,但是公司中,很多情况,是需要多入口,也就是多路由的Url,这个时候入口的配置就比较麻烦,我这里是放单独一个文件里面配置,我们公司是靠规定来执行,也就是一个文件夹所有的main.js都认为是入口文件,其他都忽略。

function getEntry(hotMiddlewareScript) { 
    var pattern = paths.dev.js + 'project/**/main.js'
    var array = glob.sync(pattern); 
    var newObj = {}; 
 
    array.map(function(el){ 
        var reg = new RegExp('project/(.*)/main.js','g'); 
        reg.test(el); 
        if (hotMiddlewareScript) { 
            newObj[RegExp.$1] = [el, hotMiddlewareScript]; 
        } else { 
            newObj[RegExp.$1] = el; 
        } 
    }); 
    return newObj; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

2、assetsInsert 是用来做模板替换的,一个小插件把template里面的值替换成打包后的css或者js。

三、打包环境配置--production

var webpack = require('webpack'); 
var path = require('path'
var merge = require('webpack-merge'
var baseConfig = require('./webpack.base'
var getEntries = require('./getEntries'
var ExtractTextPlugin = require('extract-text-webpack-plugin'); 
var assetsInsert = require('./assetsInsert'
 
var productionConf = merge(baseConfig, { 
    entry: getEntries(), 
    output: { 
        filename: './[name].[hash].js'
        path: path.resolve('./public/dist'), 
        publicPath: './' 
    }, 
    plugins: [ 
        new webpack.DefinePlugin({ 
            'process.env': { 
                NODE_ENV: '"production"' 
            } 
        }), 
        new ExtractTextPlugin('./[name].[hash].css', { 
            allChunks: true 
        }), 
        new webpack.optimize.UglifyJsPlugin({ 
            compress: { 
                warnings: false 
            } 
        }), 
        new webpack.optimize.OccurenceOrderPlugin(), 
        new assetsInsert() 
    ] 
}) 
 
productionConf.module.loaders = [ 
             { 
                test: /\.js$/, 
                loader: 'babel'
                exclude: /node_modules/ 
            }, { 
                test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf|otf)(\?.*)?$/, 
                loader: 'url?limit=8192&context=client&name=[path][name].[hash:7].[ext]' 
            }, 
            { 
                test: /\.css$/, 
                loader: ExtractTextPlugin.extract('style''css'), 
            }, 
            { 
                test: /\.scss$/, 
                loader: ExtractTextPlugin.extract('style''css!sass'
            }] 
 
module.exports = productionConf 
  • 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.

基本跟开发的差不多,差别在于:

1、使用ExtractTextPlugin 来打包css,所以要干掉原来base的loaders,重新写了一个,在最下面。

2、UglifyJsPlugin 给js压缩代码。其他没有什么好解释的了,一样的。

四、构建命令

require('shelljs/global'
env.NODE_ENV = 'production' 
var ora = require('ora'
var webpack = require('webpack'
var webpackConfig = require('./webpack.production.config'
 
var spinner = ora('building for production...'
spinner.start() 
 
var staticPath = __dirname + '/../public/dist/' 
rm('-rf', staticPath) 
mkdir('-p', staticPath) 
 
webpack(webpackConfig, function (err, stats) { 
  spinner.stop() 
  if (err) throw err 
  process.stdout.write(stats.toString({ 
    colors: true
    modules: false
    children: false
    chunks: false
    chunkModules: false 
  }) + '\n'
}) 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

写一个build.js,然后在package.json里面添加 script 参数 

"build""node build.js"//这里记得写自己build.js路径 
  • 1.

甜点(马卡龙)--有点腻

上面的配置是可以更改的,例如你在loaders 里面加上


  test: /\.vue$/, 
  loader: 'vue' 

  • 1.
  • 2.
  • 3.
  • 4.

就可以变成支持.vue文件的vuejs打包构建,同理,修改下支持jsx,和添加一些reactjs的module,就可以用来跑Reactjs的东西。

还有可以随意更改Css预编译器的类型,用你自己喜欢就行,或者跟我们前面提到的方法,把所有类型都配置上,

var path = require('path'
var config = require('../config'
var ExtractTextPlugin = require('extract-text-webpack-plugin'
 
exports.assetsPath = function (_path) { 
  return path.posix.join(config.build.assetsSubDirectory, _path) 

 
exports.cssLoaders = function (options) { 
  options = options || {} 
  // generate loader string to be used with extract text plugin 
  function generateLoaders (loaders) { 
    var sourceLoader = loaders.map(function (loader) { 
      var extraParamChar 
      if (/\?/.test(loader)) { 
        loader = loader.replace(/\?/, '-loader?'
        extraParamChar = '&' 
      } else { 
        loader = loader + '-loader' 
        extraParamChar = '?' 
      } 
      return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : ''
    }).join('!'
 
    if (options.extract) { 
      return ExtractTextPlugin.extract('vue-style-loader', sourceLoader) 
    } else { 
      return ['vue-style-loader', sourceLoader].join('!'
    } 
  } 
 
  // http://vuejs.github.io/vue-loader/configurations/extract-css.html 
  return { 
    css: generateLoaders(['css']), 
    postcss: generateLoaders(['css']), 
    less: generateLoaders(['css''less']), 
    sass: generateLoaders(['css''sass?indentedSyntax']), 
    scss: generateLoaders(['css''sass']), 
    stylus: generateLoaders(['css''stylus']), 
    styl: generateLoaders(['css''stylus']) 
  } 

 
// Generate loaders for standalone style files (outside of .vue) 
exports.styleLoaders = function (options) { 
  var output = [] 
  var loaders = exports.cssLoaders(options) 
  for (var extension in loaders) { 
    var loader = loaders[extension] 
    output.push({ 
      test: new RegExp('\\.' + extension + '$'), 
      loader: loader 
    }) 
  } 
  return output 

  • 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.

这就是把所有的css预编译的都加到配置里面了。

总结下--买单啦

Webpack多种多样,就算一个loaders都有好几种不同的配置,让人很是头疼,最关键的是很多插件自己的文档也不清不楚,弄得大家都很迷茫,我的经验就是多试多测,自己多写一写,看命令行打印的错误,去找原因,不要一看到报错就慌了,很多新手最容易犯错就是一看到报错就怀疑人生了,一定要看报错记录,一般都有提示,按照提示去解决相应问题就好啦。

责任编辑:庞桂玉 来源: segmentfault
相关推荐

2016-08-04 14:08:57

前端javascripthtml

2016-11-07 21:24:08

HtmlNode.jsJavascript

2016-11-07 21:15:12

前后端分离expressJavascript

2023-12-27 14:19:33

Python内置函数开发

2012-03-28 09:40:40

JavaScript

2021-02-04 10:22:32

前端开发技术

2018-07-20 14:30:15

2023-02-13 00:18:22

前端库框架集合

2019-09-03 11:32:36

PythonLinux操作系统

2020-11-10 07:11:23

Linux内核补丁

2017-11-07 22:25:17

网站评测工具YSlow

2024-04-01 11:52:46

2020-02-25 22:00:22

机器人人工智能系统

2013-07-05 14:45:05

AndroidOpenGL ES开发

2015-03-10 11:21:44

JavaScript组JavaScript

2018-01-10 12:09:12

Android开发程序员

2014-08-14 10:31:04

2021-01-26 10:33:45

前端开发技术

2020-06-18 10:03:13

在家工作疫情统一通信

2020-05-19 10:45:31

沙箱前端原生对象
点赞
收藏

51CTO技术栈公众号