Rails 5.1 使用 yarn 和 webpack 实战 (vue, 构建等)

之前就有听到Rails 5.1会迎来大更新, 主题是Loving Javascript,并会迎来yarn和webpack,目前前端小伙伴们最强大的工具。相信稍有关注前端发展的同学,均听过当今前端圈子里流行的种种名词: es6 es7 vue react react-native cs-modules等等, 因为node的出现,良好的包管理、服务端渲染等等新特性,另得前端的同学们写的代码在执行前有了更多的构建方式和选择,前端发展空前繁荣。然而,作为走在web开发提供最佳实践的Rails,直到5.0的版本,还是用着3.1版本时出现assets pipeline作为前端文件包管理,虽然有不少贡献者提供诸如browserify-rails vue-rails等等的gem,企图为Rails带来多少便利,但仍难改变Gem方式管理前端库的更新不及时、es6/es7新特性难支持,构建不灵便等缺陷。

我司最近其中两个项目霸屏电商,一个用到vue,一个是要求用react,正是前端交互均比较复杂的例子,使用webpack去做构建都是必须的。公司前端的小伙新技术玩得很溜,但这两个项目在开发期需要到服务端很多的交互数据去调试,最初后端和前端拆分开在两个项目里写,后来项目时间的原因后端的同学也加入到写前端代码,写完后后端同学拖代码再各种合并部署到staging测试,中间出现bug调试再上线再测试,浪费了不少时间,最后不得已地又把前端代码移到rails,又花了不少时间做项目构建, 手动把webpack和Rails前端管理体系结合,什么编译完后各种跑各种复制啊,前端里也要做环境区分啊,苦不堪言。

现在Rails 5.1出来后,一切都过去了!yarn解决了js之前模块交叉依赖、版本混乱和重复加载的问题,强大的构建工具webpack,能帮助你处理文件依赖,按需加载,还有强大的webpack-dev-server,你所能想到当前大部分前端构建和优化的问题,它有很好的解决方案。并且,它俩现在也能跟Rails很好的结合在一起了!

当然引入新的解决方案,也会带来新的问题。例如es6的语法,webpack的配置等,都会带来新的学习成本。你可能会说为啥前端要搞得那么复杂吧,但他们也是切切实实解决了很多实际问题,优化了过去很多的不足。本文不会去深入讲webpack es6等前端范畴的技术细节,webpack es6 react等,推荐大家可以去看看阮一峰老师的各种教程,深入浅出。文章引用了官方Webpacker gem内容, 本人水平有限,有问题请大家及时指出。

Rails 5.1 配置Webpack

Rails5.1新项目使要使用webpack,只需要执行

gem install rails -v '5.1'
rails new [your_project] --webpack
#也可以 rails new [your_project] --webpack=react 直接装上react

楼主是从旧项目升级,新开个rails5的git分支,项目是从4.2升级到5.1, 修改Gemfile

gem 'rails', '5.1'
gem 'webpacker'
  1. 执行bundle update rails,可能在运行的过程中会遇到一些rails 4.2时例如sass-rails, coffee-rails, rspec-rails, jbuilder之类的依赖问题,我是比较粗暴地去掉版本号跑过。

跑完后,运行rails webpacker会发现Rails已经多了自带的各种webpack命令。

jasav% rails webpacker
Available webpacker tasks are:
webpacker:install             
webpacker:compile             
webpacker:check_node          
webpacker:check_yarn          
webpacker:verify_install      
webpacker:yarn_install        
webpacker:install:react       
webpacker:install:vue         
webpacker:install:angular
  1. 执行rails webpacker:check_yarn,项目根目录便会多了一个package.jsonyarn.lock文件,熟悉node的同学知道,这是npm和yarn会用到的包管理文件,里面还可以添加一些执行命令例如scripts等

  2. 执行rails webpacker:install, 项目新建一个文件夹config/webpack,里面包含了开发环境、开发环境webpack服务器和生产环境等设置(对了, 记得在项目的.gitignore里面加入node_modules,不然会把开发环境里的安装包都提交到项目代码里了)。同时,在app目录下多了个javascript目录(注意不是app/assets/javascripts),里面还有个packs的目录,用了webpacker后,以后各种前端文件就写在这里了,区别于以前的app/assets目录。觉得config/webpack配置比较多,简单说说每个文件的作用

     ▾ webpack
       ▸ loaders  //各种文件预处理的模块,例如vue,coffee,assets等,下面的shared.js会require到webpack的module里头
         configuration.js //主要是加载paths.yml文件,定义好项目路径,包括还指定了CDN前缀
         development.js //开发环境配置文件,看到里头合并了shared.js, 还有用到sourcemap
         development.server.js //开发环境下,webpack-dev-server的运行配置文件
         development.server.yml //webpack-dev-server的host,port配置文件
         paths.yml //目录路径配置文件,例如制定了source是app/javascript
         production.js //生产环境配置文件
         shared.js //顾名思义叫shared.js,会被每个环境都合进去的一个主配置文件,例如要添加各种loader module, 或者添加plugin,都写在这里
         test.js //测试环境的配置文件
    

开发环境下使用Webpack

了解完webpack初始化后执行了的操作和生成的目录结构,下面我们来试试在开发环境里跑跑vue

  1. 上面我们看到,rails 加了直接安装vue的task, 执行
rails webpacker:install:vue

执行后我们会发现先跑了yarn add的安装指令,随后package.json文件多了vue、vue-loader、vue-template-compiler几个库,同时还贴心地在app/javascript/packs里加了两个文件: app.vue和hello_vue.js

  1. 接下来把js引入到我们的页面,在assets pipeline年代,我们写好了css和js后,会在例如layouts/application.html.erb加入<%= stylesheet_link_tag 'xxx' %>或者<%= javascript_include_tag 'xxx' %>。在webpack年代,做法也是类似,我们可以在layouts/application.html.erb 用以下标签引入静态文件:
    #这是这个示例的
    <%= javascript_pack_tag 'hello_vue' %>
    
    <-----
    #也举举官方的例子, 例如你有如下入口
    // app/javascript/packs/calendar.js
    require('calendar')
    
    #例如你有如下的目录结构
    app/javascript/calendar/index.js // gets loaded by require('calendar'),自动会读引入目录下的index.js
    app/javascript/calendar/components/grid.jsx
    app/javascript/calendar/styles/grid.sass
    app/javascript/calendar/models/month.js
    ----->
    
    <%# app/views/layouts/application.html.erb %>
    <%= javascript_pack_tag 'calendar' %>
    <%= stylesheet_pack_tag 'calendar' %>
    通过Rails的xxxx_pack_tag helper,你就可以加载好对应的静态文件
    
  2. 接下来要讲讲的是开发环境下启动项目的变化。以前我们在开发环境下一般跑rails s就够了,如果用到一些sidekiq、solr之类的库,也会多开一个窗口跑其他的进程。Rails 5.1在引入webpack后,在开发时还要启动

    shell
    bin/webpack-dev-server

    听上去多开个窗口很麻烦(用tmux, 谁用谁知道),但webpack-dev-server除了增量构建、livereload外,还有 Hot Module Replacement等好处。另外,楼主之前在用默认配置跑webpack-dev-server时,见到静态文件加载编译完日志一切正常,但用localhost发现会有请求打不进webpack的问题,只要修改config/webpack/development.server.yml的host为127.0.0.1就好,这时Rails的xxx_pack_tag helper也会自动修改host。

    hello_vue.js代码很简单,引入app.vue,然后在html body上显示个大大的"Vue"。这时我们访问http://localhost:3000就可以看到结果了。

  3. 这时我们随便改改app.vue里面的文字或css,也可以立刻见到webpack-dev-server检测到文件变动,之后页面会自动更新,cool!

其他静态文件的引入

webpack另外一个带来的好处是可以在js代码直接引入图片/svg之类的静态文件,并处理运行环境时的依赖关系,这里直接引用官方gem的例子

// React component example
// app/javascripts/packs/hello_react.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import helloIcon from '../hello_react/images/icon.png'
import '../hello_react/styles/hello-react.sass'

const Hello = props => (
  <div className="hello-react">
    < img src={helloIcon} alt="hello-icon" />
    <p>Hello {props.name}!</p >
  </div>
)

webpack遇到js文件里引入到静态文件或图片,会自动把依赖到的文件打包到output(public/packs),所以要注意,上面的文件例如引入了css文件,除了js文件外,我们在页面也要include css进来。

<%= stylesheet_pack_tag 'hello_react' %>

获取webpack静态文件的asset_pack_path helper

过去assets pipeline年代,我们可以在view里使用asset_pathimage_tag等去调用assets里面的静态文件,又或者可以在scss里使用image-url等helper,Rails会帮我们处理不同环境下对这些静态文件的引用路径问题。

现在webpacker也提供了一个asset_pack_path的helper,方便我们在不同地方引用javascirpt/pack下的静态文件,

<%= asset_pack_path 'hello_react.css' %>
<% # => "/packs/hello_react.css" %>
< img src="<%= asset_pack_path 'calendar.png' %>" />
<% # => < img src="/packs/calendar.png" /> %>

那如果我们想在webpack管理的文件里,引入过去assets pipeline(sprockets assets)里的静态文件呢? 得以于rails-erb-loader(loaders里),我们只要给js文件加个erb后缀,就可以通过helper引用回app/assets里的静态文件

// app/javascript/my_pack/example.js.erb

<% helpers = ActionController::Base.helpers %>
var railsImagePath = "<%= helpers.image_path('rails.png') %>";

生产环境deploy webpack打包

什么?原来的assets:precompile会自动跑新的taskswebpacker:compile?会自动处理页面xxxx_pack_tag引入的打包文件? webpack的production环境还会自动读取配好的cdn host? 那基本上如果你的cdn不是用回源而是用华顺ruby-china上传又拍云的方式,只需上传脚本多加个public/packs的文件夹就好了...

[稍后可能会更新(已有代码数据交互问题,例如用到fetch但要和rails_ujs和谐相处; webpack里引入module的例子,例如postcss/cssmodule;引入plugin的例子)]....

Rails 5.1令人兴奋,祝大家5.1快乐!(是巧合么...)

Leave a Reply

Your email address will not be published. Required fields are marked *