基于webpack怎么进行代码拆分

这次给大家带来基于webpack怎么进行代码拆分,基于webpack进行代码拆分的注意事项有哪些,下面就是实战案例,一起来看一下。

前言

随着前端项目的不断扩大,一个原本简单的网页应用所引用的js文件可能变得越来越庞大。尤其在近期流行的单页面应用中,越来越依赖一些打包工具(例如webpack),通过这些打包工具将需要处理、相互依赖的模块直接打包成一个单独的bundle文件,在页面第一次载入时,就会将所有的js全部载入。但是,往往有许多的场景,我们并不需要在一次性将单页应用的全部依赖都载下来。例如:我们现在有一个带有权限的”订单后台管理”单页应用,普通管理员只能进入”订单管理”部分,而超级用户则可以进行”系统管理”;或者,我们有一个庞大的单页应用,用户在第一次打开页面时,需要等待较长时间加载无关资源。这些时候,我们就可以考虑进行一定的代码拆分(code splitting)。

实现方式

简单的按需加载

代码拆分的核心目的,就是实现资源的按需加载。考虑这么一个场景,在我们的网站中,右下角有一个类似聊天框的组件,当我们点击圆形按钮时,页面展示聊天组件。

btn.addEventListener('click', function(e) {  // 在这里加载chat组件相关资源 chat.js});

登录后复制

从这个例子中我们可以看出,通过将加载chat.js的操作绑定在btn点击事件上,可以实现点击聊天按钮后聊天组件的按需加载。而要动态加载js资源的方式也非常简单(方式类似熟悉的jsonp)。通过动态在页面中添加标签,并将src属性指向该资源即可。

btn.addEventListener('click', function(e) {  // 在这里加载chat组件相关资源 chat.js  var ele = document.createElement('script');  ele.setAttribute('src','/static/chat.js');  document.getElementsByTagName('head')[0].appendChild(ele);});

登录后复制

代码拆分就是为了要实现按需加载所做的工作。想象一下,我们使用打包工具,将所有的js全部打包到了bundle.js这个文件,这种情况下是没有办法做到上面所述的按需加载的,因此,我们需要讲按需加载的代码在打包的过程中拆分出来,这就是代码拆分。那么,对于这些资源,我们需要手动拆分么?当然不是,还是要借助打包工具。下面就来介绍webpack中的代码拆分。

代码拆分

这里回到应用场景,介绍如何在webpack中进行代码拆分。在webpack有多种方式来实现构建是的代码拆分。

import()

这里的import不同于模块引入时的import,可以理解为一个动态加载的模块的函数(function-like),传入其中的参数就是相应的模块。例如对于原有的模块引入import react from ‘react’可以写为import(‘react’)。但是需要注意的是,import()会返回一个Promise对象。因此,可以通过如下方式使用:

btn.addEventListener('click', e => {  // 在这里加载chat组件相关资源 chat.js  import('/components/chart').then(mod => {    someOperate(mod);  });});

登录后复制

可以看到,使用方式非常简单,和平时我们使用的Promise并没有区别。当然,也可以再加入一些异常处理:

btn.addEventListener('click', e => {  import('/components/chart').then(mod => {    someOperate(mod);  }).catch(err => {    console.log('failed');  });});

登录后复制

当然,由于import()会返回一个Promise对象,因此要注意一些兼容性问题。解决这个问题也不困难,可以使用一些Promise的polyfill来实现兼容。可以看到,动态import()的方式不论在语意上还是语法使用上都是比较清晰简洁的。

require.ensure()

在webpack 2的官网上写了这么一句话:

require.ensure() is specific to webpack and superseded by import().

所以,在webpack 2里面应该是不建议使用require.ensure()这个方法的。但是目前该方法仍然有效,所以可以简单介绍一下。包括在webpack 1中也是可以使用。下面是require.ensure()的语法:

复制代码 代码如下:

require.ensure(dependencies: String[], callback: function(require), errorCallback: function(error), chunkName: String)

require.ensure()接受三个参数:

第一个参数dependencies是一个数组,代表了当前require进来的模块的一些依赖;

第二个参数callback就是一个回调函数。其中需要注意的是,这个回调函数有一个参数require,通过这个require就可以在回调函数内动态引入其他模块。值得注意的是,虽然这个require是回调函数的参数,理论上可以换其他名称,但是实际上是不能换的,否则webpack就无法静态分析的时候处理它;

第三个参数errorCallback比较好理解,就是处理error的回调;

第四个参数chunkName则是指定打包的chunk名称。

因此,require.ensure()具体的用法如下:

btn.addEventListener('click', e => {  require.ensure([], require => {    let chat = require('/components/chart');    someOperate(chat);  }, error => {    console.log('failed');  }, 'mychat');});

登录后复制

Bundle Loader

除了使用上述两种方法,还可以使用webpack的一些组件。例如使用Bundle Loader:

npm i --save bundle-loader

登录后复制

使用require(“bundle-loader!./file.js”)来进行相应chunk的加载。该方法会返回一个function,这个function接受一个回调函数作为参数。

let chatChunk = require("bundle-loader?lazy!./components/chat");chatChunk(function(file) {  someOperate(file);});

登录后复制

和其他loader类似,Bundle Loader也需要在webpack的配置文件中进行相应配置。Bundle-Loader的代码也很简短,如果阅读一下可以发现,其实际上也是使用require.ensure()来实现的,通过给Bundle-Loader返回的函数中传入相应的模块处理回调函数即可在require.ensure()的中处理,代码最后也列出了相应的输出格式:

/*Output format:  var cbs = [],    data;  module.exports = function(cb) {    if(cbs) cbs.push(cb);      else cb(data);  }  require.ensure([], function(require) {    data = require("xxx");    var callbacks = cbs;    cbs = null;    for(var i = 0, l = callbacks.length; i < l; i++) {      callbacks[i](data);    }  });*/

登录后复制

react-router v4 中的代码拆分

最后,回到实际的工作中,基于webpack,在react-router4中实现代码拆分。react-router 4相较于react-router 3有了较大的变动。其中,在代码拆分方面,react-router 4的使用方式也与react-router 3有了较大的差别。

在react-router 3中,可以使用Route组件中getComponent这个API来进行代码拆分。getComponent是异步的,只有在路由匹配时才会调用。但是,在react-router 4中并没有找到这个API,那么如何来进行代码拆分呢?

在react-router 4官网上有一个代码拆分的例子。其中,应用了Bundle Loader来进行按需加载与动态引入

import loadSomething from 'bundle-loader?lazy!./Something'

登录后复制

然而,在项目中使用类似的方式后,出现了这样的警告:

Unexpected ‘!’ in ‘bundle-loader?lazy!./component/chat’. Do not use import syntax to configure webpack loaders import/no-webpack-loader-syntax
Search for the keywords to learn more about each error.

在webpack 2中已经不能使用import这样的方式来引入loader了(no-webpack-loader-syntax)

Webpack allows specifying the loaders to use in the import source string using a special syntax like this:

var moduleWithOneLoader = require("my-loader!./my-awesome-module");

登录后复制

This syntax is non-standard, so it couples the code to Webpack. The recommended way to specify Webpack loader configuration is in a Webpack configuration file.

我的应用使用了create-react-app作为脚手架,屏蔽了webpack的一些配置。当然,也可以通过运行npm run eject使其暴露webpack等配置文件。然而,是否可以用其他方法呢?当然。

这里就可以使用之前说到的两种方式来处理:import()或require.ensure()。

和官方实例类似,我们首先需要一个异步加载的包装组件Bundle。Bundle的主要功能就是接收一个组件异步加载的方法,并返回相应的react组件:

export default class Bundle extends Component {  constructor(props) {    super(props);    this.state = {      mod: null    };  }  componentWillMount() {    this.load(this.props)  }  componentWillReceiveProps(nextProps) {    if (nextProps.load !== this.props.load) {      this.load(nextProps)    }  }  load(props) {    this.setState({      mod: null    });    props.load((mod) => {      this.setState({        mod: mod.default ? mod.default : mod      });    });  }  render() {    return this.state.mod ? this.props.children(this.state.mod) : null;  }}

登录后复制

在原有的例子中,通过Bundle Loader来引入模块:

import loadSomething from 'bundle-loader?lazy!./About'const About = (props) => (      {(About) => }  )

登录后复制

由于不再使用Bundle Loader,我们可以使用import()对该段代码进行改写:

const Chat = (props) => (   import('./component/chat')}>    {(Chat) => }  );

登录后复制

需要注意的是,由于import()会返回一个Promise对象,因此Bundle组件中的代码也需要相应进行调整

export default class Bundle extends Component {  constructor(props) {    super(props);    this.state = {      mod: null    };  }  componentWillMount() {    this.load(this.props)  }  componentWillReceiveProps(nextProps) {    if (nextProps.load !== this.props.load) {      this.load(nextProps)    }  }  load(props) {    this.setState({      mod: null    });    //注意这里,使用Promise对象; mod.default导出默认    props.load().then((mod) => {      this.setState({        mod: mod.default ? mod.default : mod      });    });  }  render() {    return this.state.mod ? this.props.children(this.state.mod) : null;  }}

登录后复制

路由部分没有变化


登录后复制

这时候,执行npm run start,可以看到在载入最初的页面时加载的资源如下

基于webpack怎么进行代码拆分

而当点击触发到/chat路径时,可以看到

基于webpack怎么进行代码拆分

动态加载了2.chunk.js这个js文件,如果打开这个文件查看,就可以发现这个就是我们刚才动态import()进来的模块。

当然,除了使用import()仍然可以使用require.ensure()来进行模块的异步加载。相关示例代码如下:

const Chat = (props) => (   {    require.ensure([], require => {      cb(require('./component/chat'));    });  }}>  {(Chat) => } );

登录后复制

export default class Bundle extends Component {  constructor(props) {    super(props);    this.state = {      mod: null    };  }  load = props => {    this.setState({      mod: null    });    props.load(mod => {      this.setState({        mod: mod ? mod : null      });    });  }  componentWillMount() {    this.load(this.props);  }  render() {    return this.state.mod ? this.props.children(this.state.mod) : null  }}

登录后复制

此外,如果是直接使用webpack config的话,也可以进行如下配置

output: {  // The build folder.  path: paths.appBuild,  // There will be one main bundle, and one file per asynchronous chunk.  filename: 'static/js/[name].[chunkhash:8].js',  chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js', },

登录后复制

结束

代码拆分在单页应用中非常常见,对于提高单页应用的性能与体验具有一定的帮助。我们通过将第一次访问应用时,并不需要的模块拆分出来,通过scipt标签动态加载的原理,可以实现有效的代码拆分。在实际项目中,使用webpack中的import()、require.ensure()或者一些loader(例如Bundle Loader)来做代码拆分与组件按需加载。

相信看了本文案例你已经掌握了方法,更多精彩请关注【创想鸟】其它相关文章!

推荐阅读:

vue.js怎样操作移动数组位置并且更新视图

在Vue中使用vue2-highcharts的图文详解

以上就是基于webpack怎么进行代码拆分的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2778686.html

(0)
上一篇 2025年3月8日 15:36:32
下一篇 2025年3月8日 15:36:40

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • js实现分页显示的代码

    本文主要和大家分享js实现分页显示的代码,希望能帮助到大家。 1.html页面中代码如下          登录后复制 2.HTML中js代码引用 登录后复制 $(function() {// 初始化信息paginate.use(1, 5)…

    2025年3月8日
    200
  • js表单验证实现代码

    本文主要和大家分享js表单验证实现代码,运用了正则表达式,进行匹配。希望能帮助到大家。 javascript的17种正则表达式  “^\d+$”  //非负整数(正整数 + 0) “^[0-9]*[1-9][0-9]*$”  //正整数 “…

    2025年3月8日
    200
  • 常用的js页面跳转代码

    一般来说页面的跳转可以通过a标签来进行,但是在某些情况下页面只是写了button或者其他的标签,这时我们就需要通过其他方式来实现页面的跳转。本文主要和大家分享常用的js页面跳转代码,希望能帮助到大家。 解决方案:     方式一:Locat…

    编程技术 2025年3月8日
    200
  • javascript实现文件拖拽事件的代码

    本文主要为大家详细介绍了javascript实现文件拖拽事件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能帮助到大家。 1.效果图: 2.源码    #p1 { width: 350px; height: 70px; paddi…

    2025年3月8日
    200
  • 正则表达式分组的详解(附代码)

    这次给大家带来正则表达式分组的详解(附代码),正则表达式分组的注意事项有哪些,下面就是实战案例,一起来看一下。 正则表达式分组:如果想匹配3个数字,正则表达式可以用如下写法: d{3} 以上代码使用重复量词可以匹配3位数字。但是在实际应用中…

    编程技术 2025年3月8日
    200
  • JS实现网站悬浮广告的代码

    本文主要和大家分享JS实现网站悬浮广告的代码,大家可以先看一下效果图,希望能帮助到大家。 如图 黄色区块会沿着浏览器的上下左右碰撞移动,可关闭,鼠标移上去会停止 nbsp;html>广告*{pding:0px;margin:0px;}…

    2025年3月8日
    200
  • 正则的数字验证详解(附代码)

    这次给大家带来,的注意事项有哪些,下面就是实战案例,一起来看一下。 第一部分:数值验证 1.验证只包含数字、指定长度(N)的字符串 比如我要验证只包含数字,长度为6的字符串,如123456,则可以使用下面几种效果相同的验证方式 d{6}[0…

    2025年3月8日 编程技术
    200
  • 使用MUi框架的WebService接口详解

    这次给大家带来使用MUi框架的WebService接口详解,使用MUi框架WebService接口的注意事项有哪些,下面就是实战案例,一起来看一下。 实例如下所示: mui.init(); mui.plusReady(function() …

    编程技术 2025年3月8日
    200
  • Web与AJAX的安全性能

    这次给大家带来Web与AJAX的安全性能,Web与AJAX安全性能的注意事项有哪些,下面就是实战案例,一起来看一下。 开篇三问 AJAX请求真的不安全么? AJAX请求哪里不安全? 怎么样让AJAX请求更安全? 前言 日前网络中流行围绕AJ…

    2025年3月8日 编程技术
    200
  • FormData+Ajax上传进度监控(附代码)

    这次给大家带来FormData+Ajax上传进度监控(附代码),FormData+Ajax上传进度监控的注意事项有哪些,下面就是实战案例,一起来看一下。 什么是FormData? FormData对象可以组装一组用 XMLHttpReque…

    编程技术 2025年3月8日
    200

发表回复

登录后才能评论