Rails Rails 配置 webpack

citysheep · 2015年10月02日 · 最后由 lilijreey 回复于 2016年10月08日 · 9189 次阅读

趁着国庆假期,在项目的前端搭上了 webpack,以便更好地模块化管理前端代码。这里和大家分享一下,期待更多交流。(参考了这篇业界良心的 tutorial:http://clarkdave.net/2015/01/how-to-use-webpack-with-rails

基本配置

安装 webpack

在项目的目录下创建 package.json 来管理需要的外部 module:

{
  "name": "app",
  "description": "the app",
  "version": "1.0.0",
  "dependencies": {
    "coffee-loader": "^0.7.2",
    "coffee-script": "^1.10.0",
    "exports-loader": "~0.6.2",
    "expose-loader": "~0.6.0",
    "imports-loader": "~0.6.3",
    "css-loader": "~0.6.3",
    "underscore": "~1.8.3",
    "webpack": "~1.4.13",
    "jquery": "~2.1.4"
  }
}

安装需要的 module(墙内可以使用淘宝镜像 https://npm.taobao.org/

npm install

安装 webpack

npm install -g webpack
配置 webpack

由于 webpack 本身功能的强大,配置文件也可以很复杂,这里简单配置一下,添加一个叫 webpack.config.js 的文件

var path = require('path');
var webpack = require('webpack');

var config = module.exports = {
  context: __dirname,
  // 告诉 webpack 去哪里找 entry 文件
  // entry 文件最终会被 compile 出来
  // 我们将新的 js 代码都统一放在 app/frontend/javascritps 文件夹下面 
  entry: './app/frontend/javascripts/entry.js'
};

config.output = {
  // 告诉 webpack 根据 entry 文件编译出来的文件叫 bundle.js
  // 并把文件生成到 app/assets/javascripts 目录下,这样 Rails 的 Asset Pipeline 便可以直接使用
  path: path.join(__dirname, 'app', 'assets', 'javascripts'),
  filename: 'bundle.js',
  publicPath: '/assets'
};

config.resolve = {
  // 告诉 webpack 可引用文件的后缀
  // 比如我们添加了 '.js',所以可以用 require('app') 来代替 require('app.js')
  extensions: ['', '.js', '.coffee']
};

config.module = {
  loaders: [
    // 用来 load CoffeeScript
    { test: /\.coffee$/, loader: 'coffee-loader' },
  ],
};

entry.js 可以是任何 js 文件,里面可以通过 require 来引用其他的 module,举个例子:

_ = require('underscore')
console.log(_.keys({"foo": "bar"}))
运行 webpack

运行以下命令,webpack 会根据上一步的配置文件生成一个 bundle.js

webpack -d --display-reasons --display-chunks --progress

之后只需要在 view 里引用生成的 js 文件(bundle.js)

<%= javascript_include_tag 'bundle'  %>
配置 bower

因为有些 module 只有 bower 有,所以我们可以在使用 npm 的同时也使用 bower 安装 bower

npm install -g bower

添加 bower.json

{
  "name": "app",
  "version": "1.0.0",
  "description": "the app",
  "dependencies": {
    "jquery": "~1.11.0"
  }
}

在 webpack 配置里添加 bower_components

config.resolve = {
  ...
  modulesDirectories: [ 'node_modules', 'bower_components' ],
  ...
};

再添加一个 plugin(这里感叹一下 webpack 的 plugin 相当之丰富)

config.plugins = [
  // 告诉 webpack 除了 package.json,还可以去哪里寻找 module 的描述
  new webpack.ResolverPlugin([
    new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin('bower.json', ['main'])
  ])
];

配置好之后就可以同时使用 npm 和 bower 的 package 啦

bower install
多个 entry 文件

以上的配置都只是一个 entry 文件(entry.js),有时候需要多个 entry 文件来更好分离不同页面的 js,那么可以使用以下配置:

var config = module.exports = {
  ...
  entry: {
    entry1: './app/frontend/javascripts/entry1.js',
    entry2: './app/frontend/javascripts/entry2.js
  }
  ...
};

config.output = {
  ...
  // 生成的文件直接使用 entry 的名称来命名
  filename: '[name]_bundle.js',
  ...
};

之后我们在 view 里需要的地方便可使用 entry1_bundle.js 和 entry2_bundle.js

开发环境设置

开发环境里可以使用以下的命令让 webpack 保持自动编译前端代码:

webpack --watch --colors

当然也可以使用 foreman 统一设置,比如我们项目的 Procfile:

sidekiq: bundle exec sidekiq
web: rails server
webpack: webpack --watch --colors

教程里还提到了一些高级的配置,感兴趣的同学可以试试

  • 配置全局 module,比如配置 jQuery 之后,不需要在每个文件都 $ = require('jquery')。
  • 自动提取共享 module,使用 webpack.optimize.CommonsChunkPlugin 来自动抽取不同 entry 文件里面共用的 module,生成一个新文件。
  • 生产环境可以给生成的 js 文件添加类似 Rails 的 fingerprint。

待解决问题

  • 生产环境下 Rails 会给图片添加 fingerpint,所以之前有些 js 文件添加了 .erb 后缀通过服务器渲染来使用 asset_path 调用图片。webpack 对于此类 js 似乎没有一个优雅的解决方案。目前我的做法是将这些 js 继续保留在了 Asset Pipeline 里。
  • webpack 是支持打包 css 和 image 的,还在研究如何将 Rails 里的 CSS/SASS 也使用 webpack 打包。

后记

团队目前有两名 Rubyist,本来是想等招到前端再来好好做一下模块化,但苦于厦门靠谱前端难寻,趁着国庆假期终于下决心自己撸。个人感觉 webpack 这货非常之强大,CommomJS、AMD、ES6 语法任君选择,但配置相对复杂些。这里特别感谢老同学 Strikingly CTO 郭达峰@dfguo 的推荐,也很期待下周 RubyConf 上 Strikingly 团队关于 webpack 的分享。

学习一下,正好需要

2 楼 已删除

感觉 css 没有用 webpack 的理由,继续 assets pipeline,除非结合 reactjs。

#4 楼 @tim_lang 主要有些 package 里同时有 js 和 css 的时候,不是特别想 js 在 webpack 而 css 在 asset pipeline

#5 楼 @citysheep 研究了下,sass 这种也可以 requie('xx.css') 来调用,这样再配合 gulp 这种工具,可以完全把 css 和 js 独立出去了,很赞!

我想请教一个问题,npm/bower install 之后就会把 packages 都装到当前目录的 node_modules 里,每个 project 都存一份的话冗余文件太多了。能不能像 bundler 那样系统里只存一份,每个 project 都共用系统的?

#6 楼 @tim_lang 赞啊!用 gulp 把 css/sass 生成到 assets 文件夹里面让 rails 用吗?我还在摸索怎么用 webpack 实现。

#7 楼 @nong 好问题呢,我查了下,这里有个简单的解释:http://stackoverflow.com/questions/9133784/node-version-manager-nvm-npm-installing-modules-to-common-folder

使用 npm install PACKAGE_NAME会放在当前项目目录下。个人觉得每个项目相对都是独立的,所以还好。用下面这个命令会添加到共享地方,但是仅仅 command line,项目里面 require 的时候是引用不到的。

npm install -g PACKAGE_NAME

#8 楼 @citysheep 当然了,把 assets Pipleline 的事給做了,在外旅游,不然可以上传个例子。

#10 楼 @tim_lang 赞一个,等你回来求分享 😄

@citysheep

"用 gulp 把 css/sass 生成到 assets 文件夹里面让 rails 用吗"

还不如就用 assets 自带的 webpack 对 css 的处理很反人类,是继承到 js 中,然后 js 执行的时候生成个 标签的 就算配置到 webpack 生成 manifest.json,你还得改 path_to_asset 这个 helper,使之查找自己生成的 manifest.json

我研究了下,发现集成进来的唯一地方是 webpack 生成个 xxx.bundle.js 拷贝到 vendor/assets/javascripts,然后交给 assets 处理。

@tim_lang

感觉 css 没有用 webpack 的理由

同感

#12 楼 @vkill 我感觉把 js、css、html template 这些整合在一起也逐渐主流起来了。想想有时候一个完整的前端模块确实很难把这三样东西抽离出来的。最近在学习 reactjs,还在慢慢体会它的一些新概念 😄

@citysheep

这个是纯前端的开发思路,不适合于 rails 开发

@rei

webpack 做 js 打包是非常好用的,也应该是主流了

#17 楼 @vkill 我觉得 browserify 更干净一点,和 Rails 集成也容易 https://github.com/browserify-rails/browserify-rails

@rei

学 webpack 吧,比 browserify 好 另外集成到 rails 最好的方式是 "webpack 生成个 xxx.bundle.js 拷贝到 vendor/assets/javascripts,然后交给 assets 处理。"

其他方式配置折腾下来发现还不如直接用 assets

#19 楼 @vkill browserify 可以用来作为 assets pipeline 和 npm 的桥梁,其他交给 assets。

我只是看一下,没用上。

#16 楼 @vkill 同意,如果做了前后端分离只用 Rails API 就还好。

#7 楼 @nong 为了避免每次 CI 上面的 test 都再 npm install 一次,我们现在开了一个另外的 node_modules 文件夹,然后每次跑测试都重新开一个 symbolic link 连过去。不然每次测试要跑好久。

#13 楼 @vkill css 可以用 text extraction plugin 单独抽出来成为 css 文件

#15 楼 @rei 恐怖在哪呢。。几百行的小 helper 库而已

@dfguo https://webpack.github.io/docs/stylesheets.html 用 extract-text-webpack-plugin 确实不错,thanks

#23 楼 @dfguo 听了你们的分享,回去就把 css 和 image 抽出来 😄

关于在 Rails 使用 webpack,这一篇会说的更详细一些:https://ruby-china.org/topics/29630

citysheep Rails 配置 Webpack 终极篇 提及了此话题。 06月12日 15:03

问下,wepack 和 Rails assets Pipleline 之间的关系

需要 登录 后方可回复, 如果你还没有账号请 注册新账号