Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

boarder,快速开始一个webpack项目-优化篇 #8

Open
shaodahong opened this issue Jun 26, 2017 · 9 comments
Open

boarder,快速开始一个webpack项目-优化篇 #8

shaodahong opened this issue Jun 26, 2017 · 9 comments
Labels

Comments

@shaodahong
Copy link
Owner

shaodahong commented Jun 26, 2017

在介绍篇#7 中,我大概说了下boarder这个命令行工具初始化出来的webpack项目模板有哪些功能,但是这些功能实在简单,所以想了想继续优化下,顺便解决下上篇留下来的问题

很多道友知道webpack打包现在很热门,但是不知其究竟,说实话我也不知道,上完班回去撸几把王者农药岂不快哉?但是最近webpack3又出来了,难道它想争夺版本帝?

本篇点到为止,可以根据关键字自行搜索,互联网的资料比我的更详细,易懂,有不明白的可以下面评论交流切磋,道友请留步!!!

上一篇中我已经介绍了目录结构,现在有所增强了

.
├── README.md
├── build
│   ├── webpack.base.js
│   ├── webpack.build.js
│   └── webpack.dev.js
├── dist
│   ├── index.html
│   └── static
│       └── js
├── package-lock.json
├── package.json
└── src
    ├── assets
    ├── index.html
    ├── index.js
    └── pages

可以看到src目录下增加pages这个文件夹,所以现在这个模板是支持多页面的,现在SPA大放异彩,但是有些项目中或多或少的都会涉及到其他的页面,为了方便,我们当然更希望一个npm run build后就可以发布上线了

好了,废话不多说,正题开始:

css预编译支持(stylus|less|sass)

很多项目书写css已经不是.css文件了,大多都是styl,scss和less了,所以我也要来支持下,在webpack.base.js中添加对应的loader,例如:

{
      test: /\.styl/,
      use: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: [
                 'css-loader',
                 'stylus-loader'
             ]
      }),
}

前提是你需要npm install对应的loader

ExtractTextPlugin是为了分离css的,但是开发中切勿使用,别问我怎么知道的,我的热更新啊,我的泪啊

所以它提供了一个参数可以让我们可以来禁用它,disable就是来决定是否启用,我们根据环境来判断,环境是什么,提供个关键字给你node process

new ExtractTextPlugin({
      filename: 'static/css/[name].[contenthash].css',
      allChunks: true,
      disable: isPro ? false : true
})

css预编译可以使用了,并且可以利用ExtractTextPlugin提供给我们的contenthash做长效缓存,至于hash和css的contenthash和js的chunkhash的区别自行了解,大家需要知道一般不用hash,开发中直接使用[name].js这种就行

多页面支持

假如我们除了主页还有一个aboutsetting页面,那么pages目录结构应该是这样的

├── about
│   ├── index.html
│   └── index.js
└── setting
    ├── index.html
    └── index.js

我们多页面的页面数实际开发中是不可预料的,所以我们按照约定的结构实现自动化即可,我是一个有经验的程序

/**
 * 
 * 
 * @param {string} globPath 
 * @returns {object}
 */
function getEntries(globPath) {
    var files = glob.sync(globPath),
        entries = {};

    files.forEach(function (filepath) {
        var split = filepath.split('/');
        var name = split[split.length - 2];

        entries[name] = filepath;
    });

    return entries;
}

var entries = getEntries('./src/pages/*/index.js');
var hot = 'webpack-hot-middleware/client?reload=true';

entries['index'] = './src/index.js'

我们利用glob来找到这个多页面的主入口(index.js),并且以文件夹的名字作为webpack entry的入口,最后再手动加上主页index的入口,运行起来的entries应该是这样的

{ 
about: './src/pages/about/index.js',
setting: './src/pages/setting/index.js',
index: './src/index.js' 
}

找到入口之后我们要添加到webpack的配置中

baseConfig = {
   entry: {},
   ……
}

var hot = 'webpack-hot-middleware/client?reload=true';

    Object.keys(entries).forEach(function (name) {
        baseConfig.entry[name] = isPro ? entries[name] : [hot, entries[name]];
        var htmlPlugin = new HtmlWebpackPlugin({
            filename: name + '.html',
            template: name === 'index' ? './src/index.html' : './src/pages/' + name + '/index.html',
            inject: true,
            chunksSortMode: 'dependency',
        });
        baseConfig.plugins.push(htmlPlugin);
    })

这样的话我们webpack的入口就会有index,about和setting的入口,如果有更多的页面,放马过来

解决了第一个入口问题,那我们就要解决资源的加载问题了,我们理想的状态是多页面共享的依赖剥离出来,然后每个页面的依赖和业务代码再剥离出来,这样就会有这么些文件,index.[chunkhash] .js,vendor.[chunkhash].js,index.vendor.[chunkhash].js等等,index.[chunkhash].js是主页面的业务代码,vendor.[chunkhash].js是多页面共享的依赖,index.vendor.[chunkhash].js是主页面的依赖,因为依赖的代码会很少更新,所以我们分离出来可以利用chunkhash来帮我们做到长效的缓存

长效缓存

chunkhash是根据模块的内容计算出来的hash,但是模块的ID是一个递增的数,我们并不确定依赖的数量,所以业务代码的变化也会导致依赖代码的chunkhash改变,所以我们可以使用webpack提供的HashedModuleIdsPlugin来帮我们稳定模块的ID,

new Webpack.HashedModuleIdsPlugin()

HashedModuleIdsPlugin是干什么的?HashedModuleIdsPlugin就是为了避免这种无顺序的模块ID,他是根据模块的相对路径来产生的,有了HashedModuleIdsPlugin就可以了么,还不行,因为webpack runtime代码块中是包含chunks IDchunkhash的,所以我们也要把webpack runtime代码抽出来

baseConfig.plugins.push(new Webpack.optimize.CommonsChunkPlugin({
    name: ['manifest'],
    minChunks: Infinity
}))

用一个manifest来保存webpack runtime代码,虽然会多一个文件,但是我们可以用长效缓存的优点来掩盖这一切,因为webpack runtime很少,我们没有必要单独去加载这个js,可以内联到html的头部,这个我找了下插件发现没有合适的,要么就是使用ejs,要么就是不容易变通,所以我自己借鉴了下webpack插件,写了个基于HtmlWebpackPlugin的内联资源插件html-webpack-inline-assets-plugin

new HtmlWebpackInlineAssetsPlugin({
    head: 'manifest.',
    // body: 'manifest.'
})

插件使用还是很简单的,npm install之后直接使用,两个参数,内联到head或者body中,参数是正则表达式用来匹配资源的

至此,基本上这个项目所涵盖的点都有设计,也会有不完美的地方,希望在实践中能够弥补,让它更具有扩展性

如果你有什么要求,可以去对应的项目中给我提issues
命令行工具boarder
webpack模板wdeme
资源内联插件html-webpack-inline-assets-plugin

@billyhero
Copy link

最近刚开始试着用webpack来打包项目 ,getEntries() 这个方法很不错,动态添加entery,学习了,非常感谢你的分享!

@shaodahong
Copy link
Owner Author

嗯嗯,这个boarder不好,相当于下载器,直接下载这个模板wdemo,package.json的一些参数没有改,还是wdemo的,所以不是很完善,我写这个主要就是平时想立即用webpack的时候没有好的脚手架,只能把以前的项目配置拿过来,很麻烦,所以写了个这个

@myhuangqiang
Copy link

刚开始学习webpack。。小白一枚,请教下,在开发环境下,a链接跳转路径、图片引用路径,在build之后路径就不对了,这种问题该怎么处理呀

@shaodahong
Copy link
Owner Author

@myhuangqiang 你在开发环境用的是相对路径还是绝对路径?你用绝对路径应该是可以的,相对路径的话要配置url-loader,你可以看我的url-loader配置

@myhuangqiang
Copy link

@shaodahong 非常感谢,a标签跳页面应该是根据build之后的路径把,刚试了一把,要开发环境下的目录跟打包之后href地址是不一样的^-^

@shaodahong
Copy link
Owner Author

shaodahong commented Sep 28, 2017

@myhuangqiang 是的,但是你描述的不是很清楚, 你可以上代码看下。相对路径肯定是相对于你a标签的页面的,比如你是组件,但是你build后加载是在index.html里面的,那么它会相对这个index.html来找的

@myhuangqiang
Copy link

开发环境下首页有个链接<a href="./setting/index.html">vvv</a>应该是会跳转到setting目录下的页面,但是会报找不到这个页面。写成vvv却能找到对应的页面

@myhuangqiang
Copy link

<a href="./setting.html">vvv</a>

@shaodahong
Copy link
Owner Author

@myhuangqiang 不会吧,<a href="./setting.html">vvv</a><a href="./setting/index.html">vvv</a>不是一个页面吧

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants