V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Zhiyicx
V2EX  ›  程序员

基于 Laravel 开发 ThinkSNS+ 中前端的抉择( webpack/Vue)踩坑日记

  •  
  •   Zhiyicx · 2017-05-09 15:55:36 +08:00 · 1943 次点击
    这是一个创建于 2537 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在上一篇文章《 ThinkSNS+ 基于 Laravel master 分支,从 1 到 0,再到 0.1 》中,简单的介绍了 ThinkSNS+ ,这里分享在开发过程中,前端选择我的心理活动。

    Laravel Mix 的放弃

    在 Laravel 中,前端工作流默认是由 laravel-mix 包驱动的,集成了 Vue.js。而作为核心开发之一,也负责前端这块的开发。其实,这是我第一次写 Vue,之前都是用 React 做开发。

    然后和另一个核心成员楼道抽烟聊这个事情,我们要不要用 Vue,这个东西对于我们来说,都没有做过,我们都只会 React。然后突然乔司机来了一句“玩点没玩果的嘛”!!!😄😄我们就这样决定前端使用 Vue 了。

    跑题了,决定用什么前端框架后,起初是 5.3 版本的 Laravel 前端构建有 gulp 和 webpack 都在里面,然后在 gulpfile.js 里面配置编译 js,这就不爽了呀,之前用惯了 webpack 也用惯了自动导出 css。这家伙不能从 js 里面抽离 css 单独打包,样式,js 都是单独编译的。如何能忍?但是还是忍了,因为 5.4 即将发布,因为前端不是 ThinkSNS+ 重点方向,过了很久,5.4 虽然还没正式发布,我们决定直接合并。发现 laravel-mix 也是这个样子。然后,然后,算了,删了 laravel-mix 吧,来自于开发 React 经验做构建的自信,我决定自己做前端构建。

    webpack && Vue 构建的坑

    好了,开始自己做构建了,为了保持 js 语法的统一性,我一直都是使用 webpack.config.babel.js 文件名,使用 ES6 语法配置 webpack,所以,首先依赖的包是 Babel 各个包。然后依赖进入了 Vue 包,哈哈成功了,可以转换 Vue 了。

    高兴的太早了,是的,没有达到想要的效果。style 也没有办法导出为独立 css。最后利用 vue-cli 生成了一个 example,发现这个构建也是很多问题。原因嘛,主要是不适合用在 laravel 中。example 的意义在于适合大多数情况,而我的需求就是少数情况,由此踏上了各种文档阅读之旅。

    最后在 vue-cli 中找到了答案,按照 example 的配置,去掉不需要的多余依赖,在 配置中逐步依赖,最终完成(感谢尤大神提供了这么全的配置说明)。

    mix-manifest.json

    配置是完成了,强迫症不能忍什么?使用 laravel-mix 的时候是可以使用 Laravel 的 mix 函数的,自己做构建,没法玩了。后来阅读 laravel-mix 包的代码,也没有找到答案,然后拿着 mix-manifest.json 文件反复研究,突然茅塞顿开,不就是这么一个文件的事情么?我自己生成他不就完了?

    解决方法有了,如何实现呢?起初在 webpack 配置中的实现如下:

     import { StatsWriterPlugin } from 'webpack-stats-plugin';
    function MixManifest(stats) {
      let flattenedPaths = [].concat.apply([], lodash.values(stats.assetsByChunkName));
    
      let manifest = flattenedPaths.reduce((manifest, filename) => {
        let original = filename.replace(/\.(\w{20})(\..+)/, '$2');
        manifest['/'+original] = '/'+filename;
    
        return manifest;
      }, {});
    
      // return stats;
      return JSON.stringify(manifest, null, 2);
    };
    
    ecport default {
        plugins: [
            new StatsWriterPlugin({
          filename: 'mix-manifest.json',
          transform: MixManifest
        }),
        ]
    };
    

    利用 webpack-stats-plugin 这个包,自己实现 transform 并把 文件输出到输出目录。

    这个东西一直用了接近两个月,直到后来,我们有个包 「 plus-component-web 」主要开发的是 h5 这个包就是利用 vue-cli 生成的,你想象以下配合 laravel 后,没有 watch,没有 hot,开发人员忍了一个月,每次修改完运行 yarn build 看样子,再修改。最后大功能开发完成后,调 bug,调细节,简直要人崩溃好么。

    最为公司的“前端担当”用了一个上午的时间,删了 vue-cli 生成的构建套装,自己做了一套。问题出现了,我希望这个拓展包中,可以和 ThinkSNS+ 的后台开发一样,可以使用 mix 函数怎么办?

    总不能在这个包里面也放上面的 函数+拓展生成 mix-manifest.json 文件吧?这也太不方便了点。于是决定,我要早轮子,最后在周末的时候,终于开发出了一个 webpack 插件 webpack-laravel-mix-manifest

    核心代码如下:

     import TransformFunction from './transform';
    
    class WebpackLaravelMixManifest {
    
      /**
       * 插件构造函数入口.
       *
       * @param {String} options.filename 文件名称
       * @param {Function} options.transform 转换处理方法
       *
       * @author Seven Du <[email protected]>
       */
      constructor({ filename = 'mix-manifest.json', transform = TransformFunction } = {}) {
        this.filename = filename;
        this.transform = transform;
      }
    
      apply(compiler) {
        compiler.plugin('emit', (curCompiler, next) => {
    
          // Get stats.assetsByChunkName
          // **Note**: In future, could pass something like `{ showAssets: true }`
          // to the `getStats()` function for more limited object returned.
          const { assetsByChunkName } = curCompiler.getStats().toJson();
          const mixManifestString = this.transform(assetsByChunkName);
    
          curCompiler.assets[this.filename] = {
            source: () => mixManifestString,
            size: () => mixManifestString.length
          };
    
          next();
        });
      }
    }
    
    export const transform = TransformFunction;
    
    export default WebpackLaravelMixManifest;
    

    插件的实现思路来自于 webpack-stats-plugin 这个包,非常感谢这个作者。

    然后转换方法如下:

     import path from 'path';
    
    const transform = (asstes = {}) => {
      let manifest = {};
    
      for (let name in asstes) {
        let files = asstes[name];
        if (typeof files === 'string') {
          files = [files];
        }
    
        for (let index in files) {
          const filename = files[index];
          const dirname = path.dirname(filename);
          const extname = path.extname(filename);
    
          let key = `/${dirname}/${name}${extname}`;
          if (dirname === '.') {
            key = `/${name}${extname}`;
          }
    
          manifest[key] = `/${filename}`;
        }
      }
    
      return JSON.stringify(manifest, null, 2);
    };
    
    export default transform;
    

    我知道,各位看官要吐槽了,这里为啥不用 reduce ?起初,初版真的是 reduce 实现的,代码看起来也很好。问题来了,vue 的构建都是 node 4 起步,如果用 reduce, 至少 node 6 起步了。最后妥协了,为了保证 node 4 - 7 都能运行。用了 for in 来生成。

    如果你对比过这个 webpack 插件,你一定发现了,之前我在 webpack 配置文件中写的转换函数其实是有 bug 的,例如,我入口不是对象或者数组咋办?我输出的不是 name.hash 格式怎么办?都做不到。在 webpack 插件中,解决了这个问题,最终使用如下:

     import WebpackLaravelMixManifest from 'webpack-laravel-mix-manifest';
    
    export default {
        plugins: [
            // Write out 「 mix-manifest.json 」 to build directory.
            new WebpackLaravelMixManifest()
        ]
    };
    生成的 mix-manifest.json 如下:
    
    {
      "/js/admin.js": "/js/fafe757c42b77297730a.js",
      "/css/admin.css": "/css/fafe757c42b77297730a.css",
      "/js/vendor.js": "/js/d7505bb36306d21097d9.js",
      "/js/manifest.js": "/js/291e2378932f9b50ef78.js"
    }
    

    Hot 热加载

    用了 mix 辅助函数,怎么能不提 热加载呢?在 Laravel 里面热加载是很有意思的事情。阅读 laravel-mix 后问题很简单。只要在 webpack 配置中配置如下:

     if (isHot) {
    
      // hot file.
      let hotFile = buildAssetsRoot+'/hot';
      if (fs.existsSync(hotFile)) {
        fs.unlinkSync(hotFile);
      }
      // hot reloading enabled
      fs.writeFileSync(hotFile, 'hot reloading enabled');
    }
    

    完美,ThinkSNS+,以及拓展包都惠及了,可以尽情享受 mix 辅助函数带来的便利。

    ThinkSNS+ GitHub: https://github.com/zhiyicx/thinksns-plus

    官网: http://www.thinksns.com/

    内测申请方式

    提供个人/企业联系方式及认证信息(实名 ID /企业营业执照照片或扫描件)及申请说明,发送邮件至 [email protected] 将有机会获得首批内测资格,名额有限,申请从速。

    参与内测请提供以下资料

    1 个可接收验证码的手机号

    1 个指定邮箱

    1 位测试人员姓名

    1 个您喜欢的账号昵称

    开源不易,为了争取开源,我们团队做了很多努力。把基于 Laravel 的作品展示在大家面前,之后专栏会持续不断的分享 ThinkSNS+ 开发过程中的技术细节。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2898 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 13:46 · PVG 21:46 · LAX 06:46 · JFK 09:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.