從 Laravel-mix 0.8 升級到 1.4 版的記錄與坑

部落格原文https://neighborhood999.github.io/2017/08/...

laravel-mix

相信現在許多在使用 Laravel 的朋友,應該都是使用 laravel-mix處理前端的相關資源的打包
不知道大家覺得 laravel-mix 用起來的感覺是如何?我自己覺得用起來真的沒有想像的好用,
雖然 Laravel 官方有提供文件說明操作的方法,但是我自己覺得它的擴展性很差,而且又把它再抽象化了 :skull:。

webpack

webpack 雖然設定起來比較繁瑣,但其實你會遇到的問題,可能很多人都有遇過了,
也或許有人提出相關 issue,所以要尋找一些解決方式我自己覺得相對容易,
webpack2 (現在是 webpack 3 囉!)文件相較於 webpack1 也真的友善許多 :smirk:,
老實說,我第一次看到 webpack 的文件真的覺得是天書,對新手開發者來說真的是一個痛點!
但是現在官方文件真的寫得清楚容易多了,像是 Guides 的部分我覺得就蠻不錯的,
對岸也有人將它翻譯成簡體文件,閱讀英文較吃力的朋友不妨可以參考。

升級的一些地雷

最近把公司專案的 laravel-mix 從 0.8.1 升級至目前最新的 1.4.2 版本,遇到的一些問題,
稍微做個整理和記錄,如果有打算升級 laravel-mix 的朋友,若有遇到相關問題可以參考看看 :metal:。

:bomb: 地雷一 - cross-env

比較早之前 Laravel 內的 package.json 版本的 scripts 大概是這樣:

{
  "scripts": {
    "dev": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  }
}

透過 cross-env 來解決跨平台設定 NODE_ENV 的問題,但是較早的版本是引用 node_modules 內的 js,
在新版的部分都會另外安裝 cross-env,所以設定要改成如下:

{
  "scripts": {
    "dev": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  }
}

:bomb: 地雷二 - mix.extract() & mix.autoload()

在前端,我們可能會用了不少的 library 來幫助我們做到介面上的美化,或是與使用者的互動,
但這些套件全部一次打包到一個 xxx.min.js 其實不是非常的好,當網站在載入的時候,會浪費非常多時間,
而且當你只是修改一小部分的 JavaScript 就必須要整個重新打包(包含那些 library),使用者載入網站就必須要再重新下載整個 bundle 檔,原先 cache 的檔案就沒用了!

laravel-mix 提供了一個 extract 的方法,讓你可以把一些 libray 給抽取出來,把它打包成 vendor.js
就是一個 Code Splitting 的概念,但是請記得按照以下順序把你的 JavaScript 給 inclue 到你的網頁中:

<script src="/js/manifest.js"></script>
<script src="/js/vendor.js"></script>
<script src="/js/app.js"></script>

一般在這裡會把 Vueaxioslodash 等等 library 給提取出來,我自己在這裡也把 jQuery 也給提出出來,就遇到了一些問題,你必須還要再寫一個 mix.autoload 的方法,讓你所需要的 library 自動被 include(因為所用到的 Bootstrap 依賴 jQuery),目前在 Laravel 官方文件還看不到 autoload 方法的說明,請直接到 laravel-mix - Autoloading 查看文件!

mix
  .js('resources/assets/js/app.js', 'public/js/app.js')
  .extract([
    'jquery',
    'bootstrap-sass',
    'axios',
    'vue',
    'lodash'
  ])
  .autoload({
    jquery: ['$', 'window.jQuery', 'jQuery', 'jquery'],
    vue: ['Vue', 'window.Vue'],
    axios: ['axios', 'window.axios'],
    lodash: ['lodash', 'window._']
  })
  .sass('resources/assets/sass/app.scss', 'public/css/app.css');

autoload 內是一個 object 的形式,每個 key 是你 library 名稱,value 的地方就是代表這些形式的變數會直接載入對應(key)的 libray;意思是,當我某個地方用到了 $ 或是 jQuery,webpack 會自動幫我把 library 給準備好,讓我可以使用,同理像是 Vueaxios 也是一樣的道理。

但是!我這麼做了之後發現一載入到網站還是找不到 jQuery,在看 error log 得到了以下的訊息:

// other error messages...

} else {
  // Browser globals
  factory(jQuery);
}

解決方式是需要把 window 修改為 global

- window.$ = window.jQuery = require('jquery');
+ global.$ = global.jQuery = require('jquery');

如果有遇到這樣問題的朋友,或許可以試試看這個方法。

:bomb: 地雷三:如果用到了 async/await

我在撰寫 Vue Component 的時候,用到了 async/await 的語法,在 development 模式下沒什麼問題,但是跑 CI 卻炸掉了,
在本機沒有先測試過 npm run production 的結果,在 CI 是 run production 的 script,後來 debug 找到的錯誤訊息是:

ReferenceError: regeneratorRuntime is not defined

大概知道是因為 async/await 的關係,造成在 runtime 的時候錯誤,我的解決方式再去額外安裝 babel 的 transform-runtime

$ yarn add babel-plugin-transform-runtime --dev
$ yarn add babel-runtime

為了確保在 development 和 production 都可以運作正常,所以兩個都安裝,接著建立一個 .babelrc

{
  "plugins": ["transform-runtime"]
}

plugins 內把 transform-runtime 加入進去就沒問題了。

一些使用的心得

在 Laravel 官方 mix 文件有個部分是 Copying Files & Directories,這裡主要是 mix 提供了方法讓你可以去 copy 檔案到你要的目錄下,如果是 copy node_modules 下的檔案,我覺得這有點多此一舉,其實可以用更容易的方式來做,應該可以節省一些 compile 的時間 :stuck_out_tongue:!

善用 path.join

Nodejs 內建的 path 的模組非常好用,這裡我們使用 join 的方法直接連結到 node_modules 目錄下的檔案:

const path = require('path');

mix.styles(
  [
    path.join(__dirname, 'node_modules/sweetalert/dist/sweetalert.css'),
    path.join(__dirname, 'node_modules/select2/dist/css/select2.min.css'),
  ],
  'public/css/all.css'
);

mix.combine(
  [
    path.join(__dirname, 'node_modules/sweetalert/dist/sweetalert.min.js'),
    path.join(__dirname, 'node_modules/select2/dist/js/select2.min.js'),
  ],
  'public/js/all.js'
);

這樣就可以不需要再多寫 mix.copy() 啦!順帶一提,mix.combine() 也是新的方法,類似 scripts(),但是使用 combine 方法可以幫你將必要的檔案給 bundle 並 minify,詳細可以參考文件:Concatenation and Minification

其他更多相關的方法我就沒使用過了,未來有機會用到或是遇到相關的問題再補上來。

如果文章內有任何說明上的錯誤,歡迎提出告訴我,希望大家可以一起多交流!:v:

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 4

请问怎么给 vue 组件的样式加上兼容的样式?

6年前 评论

@maxrisk 這裡指的相容的樣式是什麼?

6年前 评论

@neighborhood999 postcss啊,像 display: flex;
自动加上 -mz-display: flex; 等等

6年前 评论

我實際上沒使用過耶,但是你可以參考這篇 css-preprocessors 文件

6年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!