BccSafe's Blog Fight for a future

Webpack DllPlugin打包小技巧

前端项目构建过程中,为了提高构建效率,用上cache,往往会将第三方库和自己的业务逻辑代码分开打包,在Webpack里有两个插件可以完成这项任务,CommonsChunkDLL & DllReference

CommonsChunk

可以将相同的模块提取出来单独打包,进而减小 rebuild 时的性能消耗,但是每次构建时都需要执行一次,显然很浪费时间

DLL & DllReference

相比于前者,通过前置这些依赖包的构建,来提高真正的 build 和 rebuild 的构建效率。也就是说只要第三方库没有变化,之后的每次build都只需要去打包自己的业务代码。这个思路应该来源于Windows的动态链接库 (DLL)

如何使用DLL & DllReference在这里不再敖述,网上资料还是比较多的,可以参考https://segmentfault.com/a/1190000005770042#articleHeader11

出现的问题

使用DLL & DllReference后,第三方库的确前置构建了,但是如何让打包出来的bundle文件在index.html中引用呢?如果output.fileName写死名字,在index.html中也写死,就没有了强缓存,如果output.fileName=”[name].[hash].js”,就得找到一个往index.html添加js的办法

assets-webpack-plugin

When working with Webpack you might want to generate your bundles with a generated hash in them (for cache busting). This plug-in outputs a json file with the paths of the generated assets so you can find them from somewhere else.

有了这个插件,看起来就行得通了,在打包第三库时使用assets-webpack-plugin将bundle的文件名输出,保存成json,在打包业务代码时配合html-webpack-plugin插件,将bundle添加到index.html中

webpack.dll.config.js

const webpack = require('webpack');
const AssetsPlugin = require('assets-webpack-plugin');

module.exports = {
    entry: {
        bundle: [
            'history',
            'react', 
            'react-router', 
            'react-dom', 
            'react-redux', 
            'react-router', 
            'react-router-redux', 
            'redux',
            'redux-thunk'
            ]
    },
    output: {
    	path: '/',
        filename: '[name].[hash].js',
        library: '[name]_library'
    },
    plugins: [
        new webpack.DllPlugin({
            path: '/',      
            name: '[name]_library'
        }),
        new AssetsPlugin({
        	filename: 'bundle-config.json', 
        	path: '/'
        }),
    ]
};

webpack.config.js

const webpack = require('webpack');
const bundleConfig = require("/bundle-config.json");
module.exports = {
    entry: {
        app: ['./app.tsx']
    },
    output: {
    	path: '/',
        publicPath: '',
        filename: '[name]-[hash].js',
    	chunkFilename: '[name]-[chunkhash].js'
    },
    plugins: [
        new HtmlwebpackPlugin({
            filename: 'index.html',
            chunks: ['app'],
            template: 'app.html',
            bundleName: bundleConfig.bundle.js,
            minify: __DEV__ ? false : {
                collapseWhitespace: true,
                collapseInlineTagWhitespace: true,
                removeRedundantAttributes: true,
                removeEmptyAttributes: true,
                removeScriptTypeAttributes: true,
                removeStyleLinkTypeAttributes: true,
                removeComments: true
            }
        }),
        new webpack.DllReferencePlugin({
            context: '.',
            manifest: require('/bundle-manifest.json') 
        })
    ]
};

app.html里需要用到这样的模版 <%= htmlWebpackPlugin.options.bundleName %>

到这里,整个构建过程就比较舒服了,不重复打第三方库的包,也用上了强缓存

更详细的代码可以参考我搭的这个脚手架 https://github.com/bccsafe/React-WebApp-Boilerplate

参考资料

React移动端搭脚手架日记

来了个新项目,移动端的WebApp,自己尝试着搭建了一个脚手架出来 (因为项目催得紧,一直没有时间写,拖到现在)

  1. TypeScript 2 + Webpack + React

    在hot reload上用了React-Hot-Loader3,目前还没出正式版,参考了https://github.com/tomduncalf/typescript-react-template,其他和之前用es6没有太大差别,开发时因为需要HRM,TypeScript转换成es6,再由babel转换成es5,部署时TypeScript就直接转换成es5

  2. webpack DLLPlugin

    项目中将使用react全家桶,意味着将引入大量的包,DLLPlugin能够前置这些依赖包的构建,来提高build和rebuild的构建效率。

  3. 各种Loaders

    主要是css,sass-loader + postcss-loader,配合Autoprefixer为CSS添加浏览器特定的前缀

  4. 用typescript来写redux

    参考了http://jaysoo.ca/2015/09/26/typed-react-and-redux/

大致就是这样,有些细节和待优化的点后续补上

github地址:https://github.com/bccsafe/React-WebApp-Boilerplate

ios10,Android6下pushState导致的微信分享签名失败问题

react写的webapp,在微信里用js sdk发现在ios 10,Android6 下签名挂了,一直提示invalid signature,但是低版本的android就可以

研究了下发现是pushState导致的,签名时我们生成签名时用的url和微信取出来的url不一致导致的,比如进页面是home页,然后进了一个详情页,此时我们需要调用分享接口,这个时候我们认为url是详情页的url,但微信认为是home页的url,这就导致了问题

解决办法就是进入页面的时候将url保存起来,分享的时候用

保存进入页面最初的URL,假设为INIT_URL

根据客户端的不同:

安卓:在准备分享前(或发生URL跳转后)使用当前URL进行wx.config, 如果失败,则尝试使用INIT_URL注册

iOS:在准备分享前(或发生URL跳转后)使用INIT_URL进行wx.config, 如果失败,则尝试使用当前URL注册

参考:https://segmentfault.com/q/1010000002520634

react+redux+es6上线问题小记

最近在折腾react全家桶,项目用react+redux+es6开发,写了几个页面后准备上线,发现在chrome和iphone6上工作正常,但是到了安卓机上只给一个白屏,网页的标题倒是出来了

一度崩溃后开始找工具调试,用DebugGap发现并没有报错,页面上只有一个给react插入的DOM节点,显然ReactDOM.render这个方法没有生效

google…

发现是babel只是将es6转换成es5,而部分安卓上浏览器对es5的支持还不够,比如promise

解决办法:import 'babel-polyfill'

Babel includes a polyfill that includes a custom regenerator runtime and core.js.

This will emulate a full ES6 environment. This polyfill is automatically loaded when using babel-node and babel/register.

BccBrowser V3.0

DcefBrowser的CEF3版本近期更新到了3.2623.1401,算是个比较大的更新,顺手把BccBrowser也更新了

这个版本解决了多个之前的遗留问题,比如输入法框显示不正确,滚轮速度太快

以后DcefBrowser的内核可能不会再更新了,因为2623将是最后一个支持xp的版本,在中国这个问题可能短时间内还解决不了

下载地址: https://pan.baidu.com/s/1pLTdegN

Download URL: https://www.dropbox.com/s/3obznlfsbbvtoir/BccBrowser%20V3.0%202016-08-21.zip?dl=0

AngularJs1-ES6-Webpack 项目搭建

这份代码存在多个问题,Angular 1.x和webpack结合存在很多问题,暂时放弃...

关键字:AngularJs1, ES6, SASS, gulp, Webpack, livereload

一. 项目目录

├── gulpfile.js 存放gulp相关的配置

├── index.html 入口html

├── package.son 存放npm相关的配置

├── webpack.config.js 存放webpack相关的配置

├── css 存放scss代码

├── dist 存放打包了的js,css文件

├── js 存放es6代码

│   ├── index.js 入口文件

│   ├── compontent 组件

│       ├── module 模块组件

│       ├── CommonDirective.js 通用组件

│       └── index.js 入口文件

│   ├── config app配置

│       └── routing.js 路由

│   ├── module 模块

│       ├── test 测试模块

│       	├── index.js 入口文件

│       	├── routing.js 路由

│       	└── TestController.js 控制器

│   └── index.js 入口文件

├── view 存放html代码

│   ├── module 模块

│       ├── test 测试模块

│       	└── test.html 模版

└────

二. package.json

{
  "name": "AngularJs1-ES6-Webpack",
  "version": "0.0.1",
  "description": "A simple Demo using ES6, AngularJs1 and webpack",
  "devDependencies": {
    "angular": "~1.3.0",
    "angular-ui-router": "^0.2.14",
    "babel-core": "^6.10.4",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.9.0",
    "css-loader": "^0.23.1",
    "extract-text-webpack-plugin": "^1.0.1",
    "gulp": "^3.9.0",
    "gulp-util": "^3.0.7",
    "ng-annotate-loader": "~0.0.4",
    "node-sass": "^3.8.0",
    "raw-loader": "^0.5.1",
    "sass-loader": "^4.0.0",
    "style-loader": "^0.13.1",
    "webpack": "^1.13.1",
    "webpack-dev-server": "^1.12.1"
  },
  "author": "BccSafe",
  "license": "MIT"
}

三. AngularJs ES6写法

1) 路由

//routing.js
import TestController from './TestController.js';

export default function routing($stateProvider) {
	'ngInject';
	
    $stateProvider.state('test', {
        url: '/test',
        templateUrl: "view/module/test/test.html",
        controller: TestController
    });
}

//index.js
import 'angular';
import angularUIRouter from 'angular-ui-router';
import routing from './config/routing.js';

let myApp = angular.module("myApp", [angularUIRouter]);

myApp.config(routing);

2) Controller(Service, Factory同理)

//TestController.js
class TestController {

    constructor($rootScope, $scope, $stateParams){
        'ngInject';

        $scope.testValue = "this value from the Class TestController"; 
    }

}

export default TestController;

//index.js
import 'angular';
import angularUIRouter from 'angular-ui-router';
let testModule = angular.module('testModule', [angularUIRouter]);

export default testModule = testModule.name;

3) Directive

//CommonDirective
class CommonDirective {

    constructor(){
        'ngInject';

        this.template = '<div>I\'m a directive!</div>';
        this.restrict = "E";
    }

}

export default CommonDirective;

//index.js
import 'angular';
import CommonDirective from './CommonDirective.js';

let CommonCptModule = angular.module('CommonCptModule', []);

CommonCptModule.directive('commonDirective', () => new CommonDirective);

export default CommonCptModule = CommonCptModule.name;

四. gulp+webpack 实现打包调试

1) 判断process.env.NODE_ENV的值来区分是开发还是发布

gulp.task('set-dev-node-env', () => {
	return process.env.NODE_ENV = 'development';
});

gulp.task('set-prod-node-env', () => {
    return process.env.NODE_ENV = 'production';
});

if (process.env.NODE_ENV === 'production') {
		myConfig.plugins.push(new ExtractTextPlugin("style.css", {allChunks: true}));
}

2) Webpack模块加载器(Loaders)

loaders 用于转换应用程序的资源文件,他们是运行在nodejs下的函数 使用参数来获取一个资源的来源并且返回一个新的来源(资源的位置),例如:你可以使用loader来告诉webpack去加载一个coffeescript或者jsx

用到了babel-loader, css-loader, style-loader, sass-loader, raw-loader来完成html, es6, sass的加载, ng-annotate-loader用于添加 AngularJS依赖注入

3) Webpack开发服务器 webpack-dev-server

Webpack提供了一个基于Node.js Express框架的开发服务器,它是一个静态资源Web服务器,对于简单静态页面或者仅依赖于独立服务的前端页面,都可以直接使用这个开发服务器进行开发。在开发过程中,开发服务器会监听每一个文件的变化,进行实时打包,并且可以推送通知前端页面代码发生了变化,从而可以实现页面的自动刷新。

对HTML, Es6, SCSS文件的更改将可以不需要刷新自动更新,这点很爽

gulp.task('server', ['set-dev-node-env', 'webpack', 'myWatch'], (callback) => {
	var myConfig = Object.create(webpackConfig);
	myConfig.devtool = 'eval';
	myConfig.debug = true;
	myConfig.entry.unshift("webpack-dev-server/client?http://localhost:8080/", "webpack/hot/dev-server");

	new WebpackDevServer(webpack(myConfig), {
		publicPath: '/',
		stats: {
			colors: true
		},
		hot: true
	}).listen(8080, 'localhost', (err) => {
		if(err) throw new gutil.PluginError('webpack-dev-server', err);
		gutil.log('[webpack-dev-server]', 'http://localhost:8080/webpack-dev-server/index.html');
	});
});

查看完整代码

React Native IOS开发试水

忙了一段时间,没更新Blog,上来分享个React Native开发的天气APP,是我这学期IOS课的期末作业,没用OC的原因是想尝试下RN,之前接触的太少

上图,分别是主界面,城市列表,搜索界面,仿了苹果自带的天气APP

weatherMainView


weatherCityList


weatherSearch

初次上手RN,界面布局用了FlexBox,js方面用了最新的ES6,RN支持最新的写法,import/export处理各模块的依赖关系很爽,html则是JSX,第一次使不太习惯吧。

大概花了三天时间,整个过程还是比较有趣的,js开发原生IOS应用很棒!

AngularJS控制器继承自另一控制器

AngularJS里控制器继承,常用的就是作用域嵌套作用域。默认情况下,当前作用域中无法找到某个属性时,就会在父级作用域中进行查找,若找不到直至查找到$rootScope。

但有些情况下,rootScope下就是我们的controller,不可能将大量的公用属性方法写到rootScope里去。

比如说有多个类似的页面,都有面包屑,搜索栏,工具栏,表格等元素,面包屑表格这种元素考虑做成directive,那么必然会有许多类似的配置需要从controller传到组件里去,也会产生很多工具类方法用于处理数据等,这时候在每个页面的controller里重复写相同的代码显然很难看,就需要用到继承。

在StackOverflow上找到了解决方案,原来AngularJS已经考虑到这种情况了,提供了$controller

var app = angular.module('angularjs-starter', []); 
app.controller('ParentCtrl ', function($scope) {
  // I'm the sibling, but want to act as parent
});
app.controller('ChildCtrl', function($scope, $controller) {
  $controller('ParentCtrl', {$scope: $scope}); //This works
});

StackOverflow链接