编译资源(Mix)

简介

Laravel Mix 为应用使用常见的 CSS 和 JavaScript 预处理器提供了流畅的 API 来定义 Webpack 构建步骤。通过简单的链式调用,就可以流畅地定义一系列资源操作。例如:

mix.js('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css');

如果您曾对使用 Webpack 和资源编译感到困惑和不知所措,那么您将爱上 Laravel Mix。当然,开发应用时也不要求您一定使用它。您可以自由使用任何想用的编译工具,或者甚至不使用。

安装 & 配置

安装 Node

在运行 Mix 之前,首先要确保计算机上安装了 Node.js 和 NPM。

node -v
npm -v

默认情况下,Laravel Homestead 包含了所需的一切;当然,如果您没有使用 Vagrant,可以从 它们的下载页面 使用简单的图形化安装程序轻松地安装最新版本的 Node 和 NPM。

Laravel Mix

最后就是安装 Laravel Mix。在新安装的 Laravel 中,会在根目录下找到 package.json 文件。默认的 package.json 文件包括了所需的一切。可以把它想象成 composer.json 文件,它定义了 Node 的依赖而不是 PHP 的依赖。可以运行如下命令来安装它使用的依赖:

npm install

运行 Mix

Mix 是 Webpack 上的配置层,因此要运行 Mix 任务只需要执行包含在默认的 Laravel package.json 文件中的一个 NPM 脚本:

// 运行所有 Mix 任务
npm run dev

// 运行所有 Mix 任务并最小化输出
npm run production

监控资源文件改动

npm run watch 命令会在终端中持续运行,并监控所有相关文件的改动。Webpack 会在检测到改动后自动重新编译资源。

npm run watch

您可能发现在某些环境下,当文件改动时 Webpack 没有更新。如果系统出现这种情况,可以考虑使用 watch-poll 命令:

npm run watch-poll

使用样式表

webpack.mix.js 文件是所有编译资源的入口点。可以把它想象成 Webpack 的轻量级配置包。Mix 任务可以在一起链式调用,定义资源具体应该编译的方式。

Less

less 方法可以用来将 Less 编译为 CSS。让我们将主文件 app.less 编译到 public/css/app.css

mix.less('resources/less/app.less', 'public/css');

可以多次调用 less 用于编译多个文件:

mix.less('resources/less/app.less', 'public/css')
   .less('resources/less/admin.less', 'public/css');

如果要自定义编译生成的 CSS 的文件名,可以将文件的完整路径作为第二个参数传递给 less 方法:

mix.less('resources/less/app.less', 'public/stylesheets/styles.css');

如果需要重写 底层 Less 插件选项,可以将一个对象作为第三个参数传递给 mix.less()

mix.less('resources/less/app.less', 'public/css', {
    strictMath: true
});

Sass

sass 方法允许您将 Sass 编译为 CSS。可以像下面这样使用此方法:

mix.sass('resources/sass/app.scss', 'public/css');

同样,像 less 方法一样,可以将多个 Sass 文件编译为对应的 CSS 文件,甚至自定义生成的 CSS 的输出目录:

mix.sass('resources/sass/app.sass', 'public/css')
   .sass('resources/sass/admin.sass', 'public/css/admin');

其它 Node-Sass 插件选项 可作为第三个参数提供:

mix.sass('resources/sass/app.sass', 'public/css', {
    precision: 5
});

Stylus

与 Less 和 Sass 类似,stylus 方法允许将 Stylus 编译为 CSS:

mix.stylus('resources/stylus/app.styl', 'public/css');

也可以安装其它 Stylus 插件,例如 Rupture。首先,通过 NPM(npm install rupture) 以问答形式安装该插件,然后在调用 mix.stylus() 的时候引入它:

mix.stylus('resources/stylus/app.styl', 'public/css', {
    use: [
        require('rupture')()
    ]
});

PostCSS

Laravel Mix 自带了一个强大的开箱即用的工具 PostCSS,用于转换 CSS。默认情况下,Mix 利用流行的 Autoprefixer 插件来自动添加所有必要的 CSS3 浏览器厂商前缀。同时,您可以自由添加其它适合应用的插件。首先,通过 NPM 安装所需的插件,然后在 webpack.mix.js 文件中引用它:

mix.sass('resources/sass/app.scss', 'public/css')
   .options({
        postCss: [
            require('postcss-css-variables')()
        ]
   });

原生 CSS

如果只是想将一些原生的 CSS 样式文件合并为单个文件,可以使用 styles 方法。

mix.styles([
    'public/css/vendor/normalize.css',
    'public/css/vendor/videojs.css'
], 'public/css/all.css');

URL 处理

由于 Laravel Mix 构建于 Webpack 之上,因此了解一些 Webpack 概念非常重要。编译 CSS 时,Webpack 会重写并优化样式表中任何 url() 的调用。虽然这可能一开始听起来很奇怪,但这是一个非常强大的功能。假设我们要编译一个图片中包含相对 URL 的 Sass:

.example {
    background: url('../images/example.png');
}

任何给定的使用绝对路径的 url() 会被排除在 URL 重写之外。例如,url('/images/thing.png')url('http://example.com/images/thing.png') 不会被修改。

默认情况下,Laravel Mix 和 Webpack 会找到 example.png,将其复制到 public/images 目录,然后在生成的样式表中重写 url()。这样,编译的 CSS 会是:

.example {
  background: url(/images/example.png?d41d8cd98f00b204e9800998ecf8427e);
}

尽管此功能可能很有用,但现有的文件夹结构可能已经按您喜换的方式配置了。如果是这种情况,可以像这样禁用 url() 重写:

mix.sass('resources/app/app.scss', 'public/css')
   .options({
      processCssUrls: false
   });

添加此配置到 webpack.mix.js 文件后,Mix 不会再匹配任何 url() 或者复制资源到 public 目录。换句话说,编译后的 CSS 会和原来编写时是一样的:

.example {
    background: url("../images/thing.png");
}

资源映射

默认情况下源代码映射是禁用的,但可以通过在 webpack.mix.js 文件中调用 mix.sourceMaps() 方法来启用。尽管它会带来一些编译/性能成本,但在使用编译资源时,它会为浏览器的开发者工具提供额外的调试信息。

mix.js('resources/js/app.js', 'public/js')
   .sourceMaps();

使用 JavaScript

Mix 提供了一些功能来帮助您处理 JavaScript 文件,例如编译 ECMAScript 2015,模块捆绑,压缩和合并原生的 JavaScript 文件。更棒的是,所有这些都无缝衔接,无需任何自定义配置:

mix.js('resources/js/app.js', 'public/js');

使用这一行代码,您可以享受以下便利:

  • ES2015 语法
  • 模块
  • 编译 .vue 文件
  • 生产环境压缩代码

提取依赖库

将所有应用特定和 JavaScript 和依赖库捆绑在一起有一个潜在的缺点,会让长期缓存更加困难。例如,对应用代码的单独更改会强制浏览器重新下载所有依赖库,即使它们没有更改。

如果打算频繁更新应用的 JavaScript,应该考虑将所有依赖库提取到它们自己的文件中。这样,应用代码的更改就不会影响大的 vendor.js 文件了。Mix 的 extract 方法使之变得轻而易举:

mix.js('resources/js/app.js', 'public/js')
   .extract(['vue'])

extract 方法接收一个要提取到 vendor.js 文件的所有库或模块的数组。使用上述代码作为示例,Mix 会生成以下文件:

  • public/js/manifest.js: Webpack 运行时清单
  • public/js/vendor.js: 依赖库
  • public/js/app.js: 应用代码

为了避免 JavaScript 错误,确保按正确的顺序加载这些文件:

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

React

Mix 会自动安装支持 React 所需的 Babel 插件。首先,将 mix.js() 调用替换为 mix.react()

mix.react('resources/js/app.jsx', 'public/js');

之后,Mix 会下载并引入对应的 babel-preset-react Babel 插件。

Vanilla JS

与使用 mix.styles() 合并样式表类似,也可以使用 scripts() 方法合并并压缩任意数量的 JavaScript 文件:

mix.scripts([
    'public/js/admin.js',
    'public/js/dashboard.js'
], 'public/js/all.js');

此选项对于不需要为 JavaScript 进行 Webpack 编译的旧项目非常有用。

mix.scripts() 只有微小变化的方法是 mix.babel()。它的方法参数和 scripts 一样;但是,合并的文件会经过 Babel 编译,将任何 ES2015 代码转换为所有浏览器都能理解的 vanilla JavaScript。

自定义 Webpack 配置

默认情况下,Laravel Mix 引用了预先配置的 webpack.config.js 文件,以便尽快启动并运行。有时,您可能需要手动修改此文件。可能要引用一个特殊的加载器或插件,或者可能更喜欢使用 Stylus 而不是 Sass。这种情况下,有两种选择。

合并自定义配置

Mix 提供了一个有用的 webpackConfig 方法,允许您合并任何 Webpack 配置来覆盖默认配置。这是一个特别吸引人的选择,因为它不需要您复制和维护自己的 webpack.config.js 文件。webpackConfig 方法接收一个对象,该对象应该包含任何要应用的 Webpack 指定配置

mix.webpackConfig({
    resolve: {
        modules: [
            path.resolve(__dirname, 'vendor/laravel/spark/resources/assets/js')
        ]
    }
});

自定义配置文件

如果要完全自定义 Webpack 配置,将 node_modules/laravel-mix/setup/webpack.config.js 文件复制到项目根目录。然后,将 package.json 文件中所有 --config 的引用指向刚复制的配置文件。采用此方法进行自定义,当 Mix 有更新,必须手动将 webpack.config.js 的所有更新合并到您的自定义文件中。

复制文件 & 目录

copy 方法可用于将文件或目录复制到新位置。当 node_modules 目录中的特定资源需要复制到 public 目录时会很有用。

mix.copy('node_modules/foo/bar.css', 'public/css/bar.css');

当复制目录时,copy 方法会铺平目录结构。要保持目录原来的结构,应该使用 copyDirectory 方法:

mix.copyDirectory('assets/img', 'public/img');

版本控制/缓存清除

很多开发者会给编译的资源加上时间戳或唯一令牌后缀来强制浏览器加载新的资源,而不是提供旧的资源。Mix 中可以使用 version 方法来处理。

version 方法会自动将一个唯一哈希附加到所有编译文件的文件名后,从而更方便的实现缓存清除:

mix.js('resources/js/app.js', 'public/js')
   .version();

生成带版本的文件后,并不知道具体的文件名。因此,可以在 视图 中使用 Laravel 的全局函数 mix 来加载哈希处理后的资源。mix 函数会自动确定当前被哈希处理文件的文件名:

<link rel="stylesheet" href="{{ mix('/css/app.css') }}">

因为在开发中通常不需要带版本的文件,可以指示版本处理器只在 npm run production 时运行:

mix.js('resources/js/app.js', 'public/js');

if (mix.inProduction()) {
    mix.version();
}

Browsersync 重新加载

BrowserSync 可以自动监控文件更改,并将更改注入到浏览器,而无需手动刷新。可以通过调用 mix.browserSync() 方法启用该功能支持:

mix.browserSync('my-domain.test');

// 或者

// https://browsersync.io/docs/options
mix.browserSync({
    proxy: 'my-domain.test'
});

可以传递一个字符串(代理)或对象(BrowserSync 配置)给此方法。接下来,使用 npm run watch 命令启动 Webpack 的开发服务器。现在,当修改脚本或 PHP 文件时,浏览器会马上刷新页面并响应更改。

环境变量

可以通过在 .env 文件中为配置键名添加 MIX_ 前缀来注入环境变量到 Mix 中:

MIX_SENTRY_DSN_PUBLIC=http://example.com

.env 文件中定义此变量后,就可以通过 process.env 对象来获取了。如果在运行 watch 任务时该值发生了更改,则需要重新启动任务:

process.env.MIX_SENTRY_DSN_PUBLIC

通知

如果可用,Mix 会自动为每个包显示系统通知。无论编译成功或失败,都会提供即时反馈。不过,某些情况下您可能希望禁用这些通知。一个这样的示例可能是在生产服务器上触发 Mix。可以通过 disableNotifications 方法来停用通知:

mix.disableNotifications();