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

React 问题汇总 #6

Open
HAAAAADION opened this issue Oct 17, 2016 · 0 comments
Open

React 问题汇总 #6

HAAAAADION opened this issue Oct 17, 2016 · 0 comments
Labels

Comments

@HAAAAADION
Copy link
Owner

HAAAAADION commented Oct 17, 2016

基础文章

  1. React
  2. Redux
  3. ECMAScript 6入门
  4. React-router

css in js选型问题

css-modules

基于webpack,几乎等同传统css样式,也可以使用less sass,css样式在组件中使用后会在页面自动加载.
在做服务器端渲染的时候需要使用css-modules-require-hook,这个库能让node.js识别css文件且同步你的自定义hash类名(但也就只是获取类名让前后端渲染类名一致,具体的样式还是需要等待javascript进行加载插入
教程可以参考阮一峰老师的文章

csjs

css in js,库比较轻便,功能也比较简单,但每个class都会hash重命名,不能定于"静态全局类名",css样式可自定义获取加载

j2c

css in js,功能比较强大,拥有类似less sass的层叠样式编辑,继承,hash类名(hash后类名会带有j2c标识且较长),全局静态类名,css样式可以自定义获取加载.
但在做服务器端渲染的时候和组件引用同一份样式文件生成出来的类名却不同(hash值不同),暂时没有好的解决方法

使用原生JavaScript

示例代码:

let styles = `
    .title{
        color: #f00;
    }
`
console.log(styles);
//.title{
//    color: #f00;
//}

配合insert-css github可以把css代码插入到<head>

require('insert-css')(styles);

使用这种方法优势也很明显,不依赖第三方库,简单直接方便,开发过程中可以用css调试完后直接把css代码直接复制到let styles = ```封装,如果想实现hash值类名可以配合csjs`库来实现,原生样式用于全局样式定义,示例如下:

const csjs = require('csjs');

const styles = csjs`
    .title {
        color: #f00;
    }
`;

export default`
    ${csjs.getCss(styles)}
    .global{
        font-size: 12px
    }
`
//结果
//.title_KvcoI {
//    color: #f00;
//}
//.global{
//    font-size: 12px
//}

各种css in js库传送门

extract-text-webpack-plugin

extract-text-webpack-plugin
基于webpack打包css文件,可以搭配css-modules使用,使用后css-modules将不会自动插入css代码,需要手动加载
示例如下:

const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    //...
    module: {
        loaders: [
            {
                test: /\.css$/,
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules')
            }
        ]
    },
    //...
    plugins: [
        new ExtractTextPlugin("qqq/[name]-[chunkhash].css", {
            allChunks: true
        })
    ]
    //...
}

因为使用extract-text-webpack-plugin生成的文件可能需要带有'hash',所以在获取文件路径的时候无法静态获取,可以使用assets-webpack-plugin

assets-webpack-plugin

assets-webpack-plugin
基于webpack进行打包文件时,使用assets-webpack-plugin能获取记载的css js等文件
使用extract-text-webpack-plugin的示例进行扩展:

//打包的代码就不重复了,只需要加上`AssetsPlugin`实例就可以了
const AssetsPlugin = require('assets-webpack-plugin');

module.exports = {
    //...
    plugins: [
        new AssetsPlugin({filename: 'assets.json', path: './public/static/js-built'}),
        new ExtractTextPlugin(...)
    ]
    //...
}

webpack-isomorphic-tools

webpack-isomorphic-tools
引用别人的原话就是:

这个东西的作用就是帮你把 Webpack 为客户端处理的各种模块解析对应在服务端的实现都做好了,你按照它的说明写点代码整合到自己的架构中即可,比较快捷实用,不过也是蛮新的一个项目里面有坑几何我就没法儿说了。原文

React-router路径问题

单考虑前端的话可以使用hashHistory来做,路径样式如下
但如果要考虑同构就需要用browserHistory来代替,路径的样式如下
引用别人的原话

URL 中的 hash 部分不会发送到服务器,因此服务器端不能根据 hash 区分请求 URL,还好 react-router 支持 browserHistory + HTML5 history API 方式的路由。原文

使用browserHistory后因为用到HTML5所以会有兼容问题.
如果使用hashHistory想要去除?_k=9d95p3这个hash值可以使用外部的history库, 参考代码:

import createHistory from 'history/lib/createHashHistory'

//使用apphistory代替hashHistory
const apphistory = useRouterHistory(createHistory)({
    queryKey: false
})

其他同构文章可以参考:

  1. React服务器端渲染实践小结
  2. React同构直出优化总结
  3. (番外篇) 同构化的 React + Redux 服务端渲染
  4. React Router最新指南与异步加载实践

同构按需加载

require.ensure()

基于webpack + react-router使用require.ensure,参考代码如下:

const routes = {
    component: Init,
    childRoutes: [
        {
            path: '/',
            indexRoute: {
                getComponent: (nextState, cb) => {
                    require.ensure([], function (require) {
                        cb(null, require("../containers/essay"));
                    }, 'essay');
                }
            }
        },
        {
            path: 'posts',
            getComponent: (nextState, cb) => {
                require.ensure([], function (require) {
                    cb(null, require("../containers/posts"));
                }, 'posts');
            }
        }
    ]
};

require.ensure()会按照代码生成文件,以上代码require.ensure()会生成两份文件,分别是essay.js, posts.js最后的字符串(essay, posts)指的就是webpack.config.jschunkFilename中的[name].js
因为require.ensure()是基于webpack所以在node.js是找不到这个方法的,所以在同构的时候需要自行判断服务端与前端分别的加载方法.如果是服务端时则使用正常的require(),前端则使用require.ensure().
提供一下方法判断:

function getAsyncComponent(cb, path){
    if (typeof require.ensure != 'function'){
        cb(null, require(path));
        return false;
    }
    return true;
}

!!注意!! require.ensure()方法中的'require()'方法不能使用变量传递路径, 必须使用字符串否则会出错!

//这样是会出错的
getComponent: (nextState, cb) => {
    require.ensure([], function (require) {
        cb(null, require(path));
    }, 'essay')
}

//这样才可以
getComponent: (nextState, cb) => {
    require.ensure([], function (require) {
        cb(null, require("../containers/essay"));
    }, 'essay')
}

bundle-loader

除了上面的require.ensure()还有另一种类似的方法,就是bundle-loader,介绍可以去官方文档查看,具体用法:

function lazyLoadComponent(lazyModule) {  
  return (location, cb) => {
    lazyModule(module => cb(null, module))
  }
}

const routes = {
    component: Shell(Init),
    childRoutes: [
        {
            path: 'essay',
            getComponent: lazyLoadComponent(require('bundle?lazy&name=essay!../containers/essay'));
            }
        }
    ]
};

require前加上bundle?lazy表示这个组件需要进行异步加载,&name=essay表示生成chunk的名称
加载bundle?lazy之后的组件返回的并不是组件本身而是bundle函数,需要像上面再调用这个函数才能获取组件

另外可以不用每次调用require()都加上bundle?lazy,在webpack.config.js设置loader可以指定匹配文件都使用异步方法加载, 可以参考Implicit Code Splitting and Chunk Loading with React Router and Webpack

webpack热加载

  1. node.js + express可以使用webpack-dev-middleware
  2. node.js + koaV2可以使用koa-webpack-middleware(官方推荐方法),按照官方示例测试后,发现使用会页面无法正常显示,会自动进行下载,推测是header设置问题?具体暂时没有深入研究.
    转而使用koa-webpack-dev-middleware + koa-webpack-hot-middleware,使用方法与上面的基本一致,库语法使用koaV1,可以使用koa-convert封装使用
    参考文章:
  3. Express结合Webpack的全栈自动刷新
  4. koa中webpack热加载&&NODE_ENV配置

Babel解析问题

babel命令放到package.json里面出错
可以先更新babel相关库
非全局执行的babel命令需要添加.babelrc文件
具体配置:

{
	"presets": [
		"es2015",
		"react",
		"stage-2"
	],
	"plugins": []
}

默认解析出来的文件对export default {}支出不好导致不能正常require()加载,可以使用babel-plugin-add-module-exports来解决

使用koa + node.js搭建的服务器解析后出现以下错误:

        var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(ctx, next) {
                                     ^

ReferenceError: regeneratorRuntime is not defined
    at D:\luoo\nodejs\babel\routes\index-koa2.js:79:38
    at route (D:\luoo\nodejs\babel\routes\index-koa2.js:140:6)
    at Object.<anonymous> (D:\luoo\nodejs\babel\server.js:79:31)
    at Module._compile (module.js:541:32)
    at Object.Module._extensions..js (module.js:550:10)
    at Module.load (module.js:458:32)
    at tryModuleLoad (module.js:417:12)
    at Function.Module._load (module.js:409:3)
    at Function.Module.runMain (module.js:575:10)
    at startup (node.js:160:18)
    at node.js:449:3

这里引用阮一峰老师原话:

Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。
使用babel-polyfill库为环境提供一个垫片
在文件头加入下面代码:

import 'babel-polyfill';
// 或者
require('babel-polyfill');

最终配置:
.babelrc

{
	"presets": [
		"es2015",
		"react",
		"stage-2"
	],
	"plugins": [
		"add-module-exports"
	]
}

package.json

{
//...
  "devDependencies": {
    "babel-cli": "^6.14.0",
    "babel-core": "^6.18.2",
    "babel-eslint": "^6.1.0",
    "babel-loader": "^6.2.7",
    "babel-plugin-add-module-exports": "^0.2.1",
    "babel-polyfill": "^6.9.1",
    "babel-preset-es2015": "^6.9.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-stage-0": "^6.5.0",
    "babel-preset-stage-2": "^6.18.0",
    "babel-preset-stage-3": "^6.17.0",
  }
//...
}

参考文章:

  1. Babel 入门教程
  2. Babel 使用指南
  3. ES2015 & babel 实战:开发 NPM 模块
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

1 participant