使用NPM + Webpack进行前端开发的示例

来源:互联网 发布:mac os 10.12 原版 编辑:程序博客网 时间:2024/06/06 00:29

最近在使用Webpack来帮助进行前端开发,觉得这个东西的确挺好用。所以用一个简单的示例记录一下,供他人以及自己以后的参考。

这篇文章不是Webpack的教程贴,也不分析Webpack的优缺点,只是简单的记录我自己觉得还不错的一套用法,在阅读本文前,读者需要对NPM以及Webpack有个基础的了解。


1.项目结构



项目名称叫webpack-demo。

源代码目录是app,下面有三个文件夹,js,less(当然你也可以使用postcss或者sass等),以及html。

package.json是npm中的包管理配置文件,webpack.config.js是webpack默认的配置文件,webpack.plugin.js则是我为了使webpack.config.js看起来更清晰而提取出的一些配置内容,顾名思义是提取出了插件的配置。

node_modules是执行npm install后依赖包的安装目录。

build文件夹则是最终项目编译后的结果,其中的JS和CSS是webpack根据源代码中的依赖自动生成的一个个bundle文件,而html则是原封不动的从app中复制过去的。


二、配置分析


1.package.json

{  "name": "webpack-demo",  "version": "1.0.0",  "description": "",  "scripts": {    "product": "webpack",    "dev": "webpack"  },  "dependencies": {    "jquery": "^3.1.0"  },  "devDependencies": {    "clean-webpack-plugin": "^0.1.10",    "copy-webpack-plugin": "^3.0.1",    "css-loader": "^0.23.1",    "extract-text-webpack-plugin": "^1.0.1",    "less": "^2.7.1",    "less-loader": "^2.2.3",    "style-loader": "^0.13.1",    "webpack": "^1.13.1",    "webpack-merge": "^0.14.1"  }}

这里面主要的就是scripts和两个dependencies。之所以要分product和dev两个script,是用来区分生产和开发两种环境,这关系到webpack中的配置。

两个dependencies的内容就是开发和运行时所依赖的包,对这两个东西不太清楚的读者可以参考stackoverflow上的回答。


2.webpack配置


webpack.config.js

/** * Created by hshen on 8/15/16. */// Define pathsconst path = require('path');const PATHS = {    app: path.join(__dirname, 'app'),    build: path.join(__dirname, 'build')};// A tool to merge config objectconst merge = require('webpack-merge');// Configuration for pluginsconst plugins = require('./webpack.plugins');// Common configuration for production and devconst common = merge(        {            entry: {                page_a: path.join(PATHS.app,'js','pages','page_a.js'),                page_b: path.join(PATHS.app,'js','pages','page_b.js')            },            output: {                path: path.join(PATHS.build,'js'),                filename: '[name].js',                chunkFilename: '[chunkhash].js'            },            resolve: {                root: [                    PATHS.app                ],                alias: {                    js: 'js',                    css: 'less'                },                extensions: ['', '.js']            }        },        plugins.clean(PATHS.build),        plugins.copy(),        plugins.extractCommon('common.js'),        plugins.less());var config = null;// Detect the branch where npm is running onswitch(process.env.npm_lifecycle_event) {    case 'product':        config = merge(            common,            plugins.minify()        );        break;    case 'dev':    default:        config = merge(            common,            // Set source map for debug            {                devtool: 'source-map'            }        );        break;}module.exports = config;

根据注释大概可以知道配置是怎么工作的:
dev和product打包都需要的操作: 对每个页面的入口文件及其依赖打包成一个bundle;对路径做一些alias,方便js中的依赖;提取各个页面的公共依赖作为common;复制html;编译less。(当然还可以加上jshint等更多的功能)
对于dev,加上sourcemap用于debug;对于product,做minify的处理。

为了使配置结构更清晰,我提取了插件配置的部分到单独的webpack.plugins.js中。

webpack.plugins.js

/** * Created by hshen on 8/15/16. */const webpack = require('webpack');exports.minify = function () {    return {        plugins: [            new webpack.optimize.UglifyJsPlugin({                // Don't beautify output (enable for neater output)                beautify: false,                // Eliminate comments                comments: false,                compress: {                    warnings: false,                    // Drop `console` statements                    drop_console: true                }            })        ]    };}// Clean a specific folderexports.clean = function (path) {    const CleanWebpackPlugin = require('clean-webpack-plugin');    return  {        plugins: [            new CleanWebpackPlugin([path], {                // Without `root` CleanWebpackPlugin won't point to our                // project and will fail to work.                root: process.cwd()            })        ]    };}exports.extractCommon = function (name) {    return {        plugins: [            new webpack.optimize.CommonsChunkPlugin(name)        ]    };}exports.copy = function () {    const path = require('path');    const PATHS = {        app: path.join(__dirname, 'app'),        build: path.join(__dirname, 'build')    };    const CopyWebpackPlugin = require('copy-webpack-plugin');    return {        plugins: [            new CopyWebpackPlugin([                { from: path.join(PATHS.app,'html'), to: path.join(PATHS.build,'html')},            ], {                ignore: [                ],                // By default, we only copy modified files during                // a watch or webpack-dev-server build. Setting this                // to `true` copies all files.                copyUnmodified: true            })        ]    };}exports.less = function () {    var ExtractTextPlugin = require("extract-text-webpack-plugin");    var extractLESS = new ExtractTextPlugin('../css/[name].css',{allChunks: true});    return {        module: {            loaders: [                {                    test: /\.less$/,                    exclude: /node_modules/,                    loader:  extractLESS.extract("style-loader", "css-loader!less-loader") }            ]        },        plugins: [            extractLESS        ]    };};

三、源文件分析


a.html

<!DOCTYPE html><html><head>    <meta charset="UTF-8">    <title>Webpack demo a</title>    <link href="../css/page_a.css" rel="stylesheet" type="text/css"></head><body><script type="text/javascript" src="../js/common.js"></script><script type="text/javascript" src="../js/page_a.js"></script></body></html>

对于所有的html而言,都只需要通用的common.js以及每个页面自己单独的page_x.js。
因为html是直接被复制过去的,所以这里引的css和js都是编译和打包之后的(当然,如果对性能要求不高,可以直接用less-loader而不用extract-text插件,这样的话css就会直接内嵌到js中)。

page_a.js

/** * Created by hshen on 8/15/16. */var $ = require('jquery');var component = require('js/components/ComponentA');require('css/page_a.less');$('body').append(component);

这里因为配过了alias,所以就不用写丑陋的../之类的相对路径了。

为了不让css内嵌在js中,使浏览器可以平行加载js和css,从而提高性能,我使用了extract-text插件。
用extract-text插件的一个坏处: 我除了在html中手动去引css,还要在js中去引编译前的less。这样很不利于代码的维护。如果有读者知道怎么解决这个问题,请不吝赐教!

四、为什么不一起使用Bower、Grunt等


对于包管理,我觉得NPM一个就足够了,再额外使用Bower有点多此一举,另外还增加了配置的复杂度。
对于Grunt等Task Runner,Webpack本身已经有足够多的插件可以让我们去做很多事情,再把Grunt加上去也会增加复杂度。对于插件没有提供的东西,其实自己写点NPM的script也是可以解决的。
当然我也很期待读者能够给我不一样的看法!

1 0
原创粉丝点击