Open eyasliu opened 8 years ago
屌屌屌
要是三级路由怎么办呢?
@wefiy 不管是第几级,一直嵌套下去就是了,写法是一样的
<Route path="......" getComponent={handler}></Route>
但是三级路由写了,this.props.children为undefined,导致页面无法渲染,大神求解答
@xiaoji201509 你确定是这样子写的吗
<Route path="blog" getComponent={(location, callback) => {
require.ensure([], require => {
callback(null, require('modules/blog'))
}, 'blog')
}}></Route>
注意Route的props是getComponent而不是component,值是一个函数,在函数里面使用 require.ensure
去指定组件
代码结构大概是这样的。TaskRoute 在这个里面取不到 this.props.children,但是在AllRoute 取得到。
class AllRoute extends React.Component{
render() {
return (
<div >
{this.props.children}
</div>
);
}
}
class TaskRoute extends React.Component{
constructor(props){
super(props);
}
render() {
return (
<div >
{ this.props.children}
</div>
);
}
}
class ListRoute extends React.Component{
constructor(props){
super(props);
}
render() {
return (
<div >
{ this.props.children}
</div>
);
}
}
const Success = (location, callback) => {
require.ensure([], require => {
callback(null, require('./components/Success'))
}, 'Success')
}
<Provider store={store}>
<Router history={history}>
<Route path="/" component={AllRoute}>
<Route path="test1" component={TaskRoute}>
<Route path="success" getComponent={Success}/>
</Route>
<Route path="test2" component={ListRoute}>
.........
</Route>
</Route>
</Router>
</Provider>
@xiaoji201509 这么看来代码是没什么问题的,确定做到下面这几点没有
/test1/success
output.chunkFilename
配置Success
这个 chunk本地热加载是出现了这个chunk的,但是就是没执行。也没渲染
问题解决了,require('./components/Success')改成这样就可以了require('./components/Success').default,
你好,我在 webpack.config.js 中定义了 chunkFilename 的命名方式,可是实际生成的 chunkfile 中还是有 id,请问你有遇到过这个问题吗?
我是想生成 [name].[chunkhash:8].chunk.js
这样格式的文件,可是实际生成的是 [id].[name].js
这样的文件。
我的 react-router 的代码如下:
var Movies = function(location, callback) {
require.ensure([], function(require) {
callback(null, require('./movies.jsx'));
}, 'movies');
};
var Movie = function(location, callback) {
require.ensure([], function(require) {
callback(null, require('./movie.jsx'));
}, 'movie');
};
var Books = function(location, callback) {
require.ensure([], function(require) {
callback(null, require('./books.jsx'));
}, 'books');
};
var Book = function(location, callback) {
require.ensure([], function(require) {
callback(null, require('./book.jsx'));
}, 'book');
};
ReactDOM.render((
<Router history={hashHistory}>
<Route path="/" component={App}>
<Route path="movies" getComponent={Movies} />
<Route path="/movie/:id" getComponent={Movie} />
<Route path="books" getComponent={Books} />
<Route path="/book/:id" getComponent={Book} />
</Route>
</Router>
),
document.getElementById('main')
);
webpack.config.js 的 output 代码:
output: {
path: './dist',
filename: '[name].js',
chuckFilename: '[name].[chunkhash:8].chunk.js',
publicPath: './dist/'
},
命令行生成的 chunkfile如下:
@cobish
你写错单词了,正确是 chunkFilename
你写成了 chuckFilename
@eyasliu 噢原来如此,真是太感谢你了!
很强!谢谢!
谢谢 有用
呵呵不错。
谢谢,很有用🙏
在开发过程当中还遇到一个问题:
如果有2个异步加载的页面:
require.ensure([], function() {
require('modules/A');
})
require.ensure([], function() {
require('modules/B');
})
其中A,B
模块都共同引用了模块C
,那么在打包过程中,webpack
会将A,C
打包在一起,同时还会在B,C
打包在一起。
虽然webpack
提供了CommonChunkPlugin
插件,但是这个插件是将entry
里面的共同的模块抽离出来打包。它没法去分析require.ensure([], funciton() {})
异步加载模块里面的共同模块,然后去打包。
这样就造成了重复打包的情况。请问遇到这种问题,有什么比较好的方法去解决呢?
@CommanderXL 你可以在基础包中引用一下 C 包,这样就会将 C 包打进基础包中,在 A 和 B 模块,就不会在将 C 打包进去了。或者像 @cobish 给的 demo 那样也行,专门用一个 entry 来打包需要重复用到的模块,不过这样会多出一个需要手动引入的包,但是这样对于以后的增量升级也是有好处的
@cobish @eyasliu 对应到具体的业务上来看的话,我的理解是将一写工具模块可以单独打一个包,可以放到entry
里面引入,具体到不同页面的业务逻辑的话,可以通过require.ensure([], function(){})
这种方式进行按需加载。这种方式是否合理呢?
@CommanderXL 这种方式是可以的,将一些完全跟业务逻辑无关的工具模块打一个包,可以跨项目使用。将有业务逻辑的模块打包成各个小模块按需加载
请教一下各位,我有个404页面component,import了一个less文件(index.less)代码如下:
react-router的配置如下:
执行webpack确没有生成404.css的chunk文件,我想请问是哪里有问题?谢谢!我已经用extract-text-webpack-plugin来处理import进来的less文件。
想问下 我按这样写了之后 一切是正常运行的 但是 怎么样 看出项目是按需加载的?
我发现 我这样写了之后 并没有 按需加载啊。。。是什么情况? 我的项目
解决了,自己代码的问题,在路由这里异步加载过,就不需要在其他地方 同步加载了,否则会自动去掉异步的方法
webpack.config.js 的 entry 该如何配置呢?
@FengHaiSheng entry不需要其他特殊配置
@eyasliu 非常感谢回答。我照着教程试了,发现确实达到了按需加载的功能。只有一点比较不懂,虽然做了按需加载,但是以前那个文件(所有代码都打包到了这个文件)仍然被加载了(我在network中看到的)
childRoutes: [{
path: '/welcome',
getComponents: (location, callback) => require.ensure([], require => {callback(null, require("./components/welcome/Welcome.react.jsx").default)},'welcome')
},{
path: '/menu',
getComponents: (location, callback) => require.ensure([], require => {callback(null, require("./components/menu/MenuMain.react").default)},'menu')
},{
path: '/combo/:menuId/:isInclude/:combo_carts/:menu_item',
getComponents: (location, callback) => require.ensure([], require => {callback(null, require("./components/menu/MenuComboMain.react").default)},'combo')
}]
这样配置, 除了'/menu' 剩下的进入对应的路由都有单独的文件生成,唯独menu
没有生成单独的文件,在 打包好的文件里搜 menu 组件的内容,发现在输出的那个文件中. 这是怎么回事呢,写法完全相同啊
@mqliutie 可能是menu里面所有引用的包都在基础包中引用过,所以menu就不需要了
@eyasliu 不会的,menu这个组建里面有我自定义的组建,其他文件中没有引入的
你好,我有一个项目,entry入口,有8个路由页面,我没有用按需加载的时候直接在index.html里引入bundle.js,bundle.js大小为2.8M。 如果我用了按需加载后,我还需要在index.html引用bundle.js吗?如果不引用的话,页面没有加载任何js,如果引用了,network里的先加载bundle.js(2.2M),再引入对应页面的chunk.js。
这样是正常吗?
@GZWZC 我认为是正常的 我的页面也是这样的
这个很详细。
注意: 或许有人会想,上面重复代码超级多,能不能用一个函数生成器去生成这些重复的函数呢?代码更进一步优化,比如:
const ensureModule = (name, entry) => (location, callback) => { require.ensure([], require => { callback(null, require(entry)) }, name) }
———————————————— 我看到上面代码后还挺高兴的:“这多方便”,后来才看到:
答案是:不能。这样看起来代码没有任何问题,好像更优雅的样子,但是经过亲自实践后,不行!!
非常感谢老师的分享,这个帖子给我解开了积压许久的困惑。 通过这个文章,经过几番尝试,终于把路由拆分了。 这个帖子开头的部分的一些理论铺垫起到了由其关键的作用。 这个帖子是真的从“头”讲起,由“浅”入深。真希望所有的教程都像这篇一样有基础理论铺垫。 再次感谢您的分享。3Q 好人一生平安,老司机永远顺风……
@CommanderXL 你好,我也遇到了你上述问到的代码分割后多次引入模块(比如echart)重复打包的问题,请问,你这边最后用什么方法处理的?
css重复问题 怎么处理
厉害了
666
弱弱问句,就是每次webpack打包后会生成对应的文件,但是再执行一次webpack命令后又重新打包了,打包出来的文件就是命名的hash值不一样,这个怎么解决。。 就是类似于这种,我执行了一次webpack命令后,退出后又执行了两次,就打了三次包
请问一下,我用webpack配置了一个多页面的开发环境,但是我又在一个页面中用路由的形式来配一个路由页面,如果不用懒加载js的话是正常的,但是如果用了懒加载的,发现js没有打包出来。
//首页 const index = (location, callback) => { require.ensure([], require => { callback(null, require('./containers/lehu.h5.container.index').default) }, 'index') }; //分类 const classify = (location, callback) => { require.ensure([], require => { callback(null, require('./containers/lehu.h5.container.classify').default) }, 'classify') }; let Indexs = document.getElementById('index'); render(
@yinguangyao 可以用 clean-webpack-plugin 在打包前清楚之前打包的文件重新生成。
output: { filename: '[name]-[chunkhash].js', publicPath: BUILD_PATH },
然后如果打包多个,配置output的filename 是 [name]-[chunkhash].js 格式。
然后用webpack的HashedModuleIdsPlugin插件,嗯,可以检测不变动未经修改的文件的hash名。
const ensureModule = (name, entry) => (location, callback) => { require.ensure([], require => { callback(null, require(entry)) }, name) }
现在函数里包裹 require
会报下面的错误,有解决的办法吗?
Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
异步组件a中再异步加载b会怎样,还是打出一个包a还是两个包a和b
组件库很庞大,但是用到了某些独立的依赖,并且这些依赖随时可以用到,但是又不会经常使用,而且体积也比较大。这种情况没办法使用路由的方式按需加载,必须判断对应的部分是否使用再进行加载。有招吗
@SouWinds 新版本的webpack有 import 函数可以做按需加载,你可以这样
// 在需要使用那个组件的时候,才执行import
import('./your/mod').then(() => {
// 组件模块加载完成
})
@SouWinds 新版本的webpack有 import 函数可以做按需加载,你可以这样
// 在需要使用那个组件的时候,才执行import import('./your/mod').then(() => { // 组件模块加载完成 })
我试试看,我查了下文档,要最小化搜索范围、缩小变量控制区域
webpack 懒加载使用 import 引入 js 文件么有生成 chunk 文件
为什么需要按需加载
在一个前端应用中,将所有的代码都打包进一个或几个文件中,加载的时候,把所有文件都加载进来,然后执行我们的前端代码。只要我们的应用稍微的复杂一点点,包括依赖后,打包后的文件都是挺大的。而我们加载的时候,不管那些代码有没有执行到,都会下载下来。如果说,我们 只下载我们需要执行的代码的 话,那么可以节省相当大的流量。也就是我们所说的 按需加载
使用 webpack 的按需加载
webpack 官方文档 其实是有介绍的,不过我还是啰嗦的在总结一下
首先我们要看一看一个加载函数
这个方法可以实现js的按需加载,分开打包,webpack 管包叫
chunk
,为了打包能正常输出,我们先给webpack配置文件配置一下chunk文件输出路径这里顺带一提,打包后的js文件基础路径跟普通的资源(图片或字体文件之类)是一样的,就是publicPath, publicPath可以在运行时再去赋值,方法就是在应用入口文件对变量
__webpack_public_path__
进行赋值就行,文档在这每个chunk 都会有一个ID,会在webpack内部生成,当然我们也可以给chunk指定一个名字,就是 require.ensure 的第三个参数
配置文件中
最简单的例子
将会打包出 3 个文件,基础包、chunk1 和 chunk2,但是chunk2在if判断中,而且永远为false,所以 chunk2 虽然打包了但永远不会被加载
结合 react-router 按需加载
如果需要做按需加载,那么这个
需
应该怎样定义呢?我们可以按照前端路由来定义这个需
,在react 应用中,react-router 是一个路由解决方案的第一选择,它本身就有一套动态加载的方案看他们的方法名字就知道他们是干什么的,我也不废话。他们的作用呢,就是在访问到了对应的路由的时候,才会去执行这个函数,如果没有访问到,那么就不会执行。那么我们把加载的函数放在里面就正好合适了,等到访问了该路由的时候,再去执行函数去加载脚本。
根路由
跟路由有点特殊,它一定要先加载一个组件才能渲染,也就是说,在跟路由不能使用按需加载方式,不过这个没关系,根路由用于基础路径,在所有模块都必须加载,所以他的 "需" 其实作用不大。
示例代码
官方有个很简易明了的示例应用, react-router 默认是推荐使用对象去定义路由而不是 jsx,所以这个例子演示了怎么使用 对象的形式定义按需加载模块。
jsx 定义按需加载路由
虽然官方推荐使用对象去定义,但是jsx语法看上去更清晰点,所以还是使用jsx演示,方法很简单,就是把 组件的 props.component 换成 props.getComponent ,函数还是上述例子的函数(记得根路由不要使用getComponent)。
看上去很乱有木有,在jsx中写那么多 js 感觉真难看,把 js 独立出来就是:
这样整理一下,就好看多了
注意: 或许有人会想,上面重复代码超级多,能不能用一个函数生成器去生成这些重复的函数呢?代码更进一步优化,比如:
答案是:不能。这样看起来代码没有任何问题,好像更优雅的样子,但是经过亲自实践后,不行!!因为
require
函数太特别了,他是webpack底层用于加载模块,所以必须明确的声明模块名,require函数在这里只能接受字符串,不能接受变量 。所以还是忍忍算了