红蓝

红蓝 查看完整档案

成都编辑电子科技大学  |  计算机 编辑  |  填写所在公司/组织 www.clannadbred.com 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

红蓝 赞了回答 · 10月2日

解决javascript函数自执行里的this为什么指向window

因为js没有类,只有函数, 函数是可以谁都能来调用的,谁调用了函数,this就是谁, 也可以用A调用函数 然后apply/call(B,...) 直接把B当成this进去 js的this很无厘头 就好比打工的码农是干活的函数 ,今天在百毒打工,归李艳红指挥, 李艳红就是this, 明天码农又换一个腾讯公司打工,马花腾就是this, 还有一种码农是外派的,就好比码农本来是是华为的员工,派到阿里去干活了,码农.apply/call(阿里,...) 这样马云就成了this

关注 22 回答 12

红蓝 回答了问题 · 2018-08-21

chrome断点调试时,经常出现很多vm开头的文件,隐身模式却没有,请问如何屏蔽?

一些为被绑定到具体文件名的代码,比如eval当中的函数,chrome为了方便人调试就集中这些代码到了vm开头的一个本来不存在文件当中

关注 5 回答 5

红蓝 赞了回答 · 2018-08-21

chrome断点调试时,经常出现很多vm开头的文件,隐身模式却没有,请问如何屏蔽?

与插件无关= =

https://jsfiddle.net/avm5bvn6/1/

VM 开头的信息可能来自几个地方:

  • eval 中的语句

  • DOM 中内嵌的事件代码

关注 5 回答 5

红蓝 赞了回答 · 2018-08-02

解决babel的polyfill和runtime的区别

babel-polyfill 使用场景

Babel 默认只转换新的 JavaScript 语法,而不转换新的 API。例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译。如果想使用这些新的对象和方法,必须使用 babel-polyfill,为当前环境提供一个垫片。

babel-runtime 使用场景

Babel 转译后的代码要实现源代码同样的功能需要借助一些帮助函数,例如,{ [name]: 'JavaScript' } 转译后的代码如下所示:

'use strict';
function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }
  return obj;
}
var obj = _defineProperty({}, 'name', 'JavaScript');

类似上面的帮助函数 _defineProperty 可能会重复出现在一些模块里,导致编译后的代码体积变大。Babel 为了解决这个问题,提供了单独的包 babel-runtime 供编译模块复用工具函数。

启用插件 babel-plugin-transform-runtime 后,Babel 就会使用 babel-runtime 下的工具函数,转译代码如下:

'use strict';
// 之前的 _defineProperty 函数已经作为公共模块 `babel-runtime/helpers/defineProperty` 使用
var _defineProperty2 = require('babel-runtime/helpers/defineProperty');
var _defineProperty3 = _interopRequireDefault(_defineProperty2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var obj = (0, _defineProperty3.default)({}, 'name', 'JavaScript');

除此之外,babel 还为源代码的非实例方法(Object.assign,实例方法是类似这样的 "foobar".includes("foo"))和 babel-runtime/helps 下的工具函数自动引用了 polyfill。这样可以避免污染全局命名空间,非常适合于 JavaScript 库和工具包的实现。例如 const obj = {}, Object.assign(obj, { age: 30 }); 转译后的代码如下所示:

'use strict';
// 使用了 core-js 提供的 assign
var _assign = require('babel-runtime/core-js/object/assign');
var _assign2 = _interopRequireDefault(_assign);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var obj = {};
(0, _assign2.default)(obj, {
  age: 30
});

思考:babel-runtime 为什么适合 JavaScript 库和工具包的实现?

  1. 避免 babel 编译的工具函数在每个模块里重复出现,减小库和工具包的体积;

  2. 在没有使用 babel-runtime 之前,库和工具包一般不会直接引入 polyfill。否则像 Promise 这样的全局对象会污染全局命名空间,这就要求库的使用者自己提供 polyfill。这些 polyfill 一般在库和工具的使用说明中会提到,比如很多库都会有要求提供 es5 的 polyfill。在使用 babel-runtime 后,库和工具只要在 package.json 中增加依赖 babel-runtime,交给 babel-runtime 去引入 polyfill 就行了;

总结:

  1. 具体项目还是需要使用 babel-polyfill,只使用 babel-runtime 的话,实例方法不能正常工作(例如 "foobar".includes("foo"));

  2. JavaScript 库和工具可以使用 babel-runtime,在实际项目中使用这些库和工具,需要该项目本身提供 polyfill;

疑问:像 antd@2.x 这样的库使用了 babel-runtime,在实际项目中使用 antd@2.x,我们需要引入 babel-polyfill。但全部 polyfill 打包压缩下来也有 80kb 左右,其中很多 polyfill 是没有用到的,如何减少体积呢?手工一个个引入使用到的 polyfill,似乎维护成本太高!

参考文献

关注 42 回答 6

红蓝 赞了回答 · 2018-08-02

解决babel的polyfill和runtime的区别

babel-runtime 是供编译模块复用工具函数。是锦上添花
babel-polyfil是雪中送炭,是转译没有的api.

关注 42 回答 6

红蓝 收藏了问题 · 2018-08-02

babel的polyfill和runtime的区别

谁能详细说说这两者的关系?
一开始只用了transform-runtime,后来发现有些es6的方法不能用,比如字符串的includes。后来又在入口文件加上babel-polyfill,能用了。
但不知道babel-runtime能否去掉,babel-polyfill和babel-runtime功能是否有重复?

红蓝 关注了专栏 · 2018-07-17

FE-杂货铺

这边的粗粮,20块一斤;那边的狗粮,很,甜。

关注 24

红蓝 赞了文章 · 2018-07-17

前端路由的前生今世及实现原理

??

原文发于我的博客:https://github.com/hwen/blogS...

什么是路由

路由这个概念最先是后端出现的。在以前用模板引擎开发页面时,经常会看到这样

http://hometown.xxx.edu.cn/bbs/forum.php

有时还会有带.asp.html的路径,这就是所谓的SSR(Server Side Render),通过服务端渲染,直接返回页面。

其响应过程是这样的

1.浏览器发出请求

2.服务器监听到80端口(或443)有请求过来,并解析url路径

3.根据服务器的路由配置,返回相应信息(可以是 html 字串,也可以是 json 数据,图片等)

4.浏览器根据数据包的Content-Type来决定如何解析数据

简单来说路由就是用来跟后端服务器进行交互的一种方式,通过不同的路径,来请求不同的资源,请求不同的页面是路由的其中一种功能。

前端路由的诞生的缘由

前端路由的出现要从 ajax 开始,为什么?且听下面分析 (ˉ▽ ̄~)

Ajax,全称 Asynchronous JavaScript And XML,是浏览器用来实现异步加载的一种技术方案。在 90s 年代初,大多数的网页都是通过直接返回 HTML 的,用户的每次更新操作都需要重新刷新页面。及其影响交互体验,随着网络的发展,迫切需要一种方案来改善这种情况。

1996,微软首先提出 iframe 标签,iframe 带来了异步加载和请求元素的概念,随后在 1998 年,微软的 Outloook Web App 团队提出 Ajax 的基本概念(XMLHttpRequest的前身),并在 IE5 通过 ActiveX 来实现了这项技术。在微软实现这个概念后,其他浏览器比如 Mozilia,Safari,Opera 相继以 XMLHttpRequest 来实现 Ajax。(? 兼容问题从此出现,话说微软命名真喜欢用X,MFC源码一大堆。。)不过在 IE7 发布时,微软选择了妥协,兼容了 XMLHttpRequest 的实现。

有了 Ajax 后,用户交互就不用每次都刷新页面,体验带来了极大的提升。

但真正让这项技术发扬光大的,(。・∀・)ノ゙还是后来的 Google Map,它的出现向人们展现了 Ajax 的真正魅力,释放了众多开发人员的想象力,让其不仅仅局限于简单的数据和页面交互,为后来异步交互体验方式的繁荣发展带来了根基。

而异步交互体验的更高级版本就是 SPA(那么问个问题,异步交互最高级的体验是什么?会在文末揭晓)—— 单页应用。单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。

单页应用的概念是伴随着 MVVM 出现的。最早由微软提出,然后他们在浏览器端用 Knockoutjs 实现。但这项技术的强大之处并未当时的开发者体会到,可能是因为 Knockoutjs 实现过于复杂,导致没有大面积的扩散。

同样,这次接力的选手依然是 Google。Google 通过 Angularjs 将 MVVM 及单页应用发扬光大,让前端开发者能够开发出更加大型的应用,职能变得更大了。(不得不感慨,微软 跟 Google 都是伟大的公司)。随后都是大家都知道的故事,前端圈开始得到了爆发式的发展,陆续出现了很多优秀的框架。

本来还想写更多的。。。不过有点慢慢偏离主题了(下次会专门写写)

从 vue-router 来看前端路由实现原理

前端路由的实现其实很简单。

本质上就是检测 url 的变化,截获 url 地址,然后解析来匹配路由规则。

但是这样有人就会问:url 每次变化都会刷新页面啊?页面都刷新了,JavaScript 怎么检测和截获 url?

在 2014 年之前,大家是通过 hash 来实现路由,url hash 就是类似于

https://segmentfault.com/a/1190000011956628#articleHeader2

这种 #。后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发 hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。

让我们来整理思路,假如我们要用 hash 的模式实现一个路由,那么流程应该是这样的。

hash-mode

hash 的实现相对来说要简单方便些,而且不用服务器来支持。

另外我们可以参考参考 vue-router 这一部分的实现(为了便于解释我简化了部分代码)

vue-router hash 实现源码地址

/**
 * 添加 url hash 变化的监听器
 */
setupListeners () {
  const router = this.router

  /**
   * 每当 hash 变化时就解析路径
   * 匹配路由
   */
  window.addEventListener('hashchange', () => {
    const current = this.current
    /**
     * transitionTo: 
     * 匹配路由
     * 并通过路由配置,把新的页面 render 到 ui-view 的节点
     */
    this.transitionTo(getHash(), route => {
      replaceHash(route.fullPath)
    })
  })
}

检测到 hash 的变化后,就可以通过替换 DOM 的方式来实现页面的更换。

14年后,因为HTML5标准发布。多了两个 API,pushStatereplaceState,通过这两个 API 可以改变 url 地址且不会发送请求。同时还有 onpopstate 事件。通过这些就能用另一种方式来实现前端路由了,但原理都是跟 hash 实现相同的。用了 HTML5 的实现,单页路由的 url 就不会多出一个#,变得更加美观。但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。具体可以见:HTML5 histroy 模式

同样,我们来理清下思路,这样写起代码才更得心应手~

html5-mode

这部分 vue-router 的源码,可以发现实现的思路大体也是相同的

export class HTML5History extends History {
  constructor (router, base) {
    super(router, base)
    /**
     * 原理还是跟 hash 实现一样
     * 通过监听 popstate 事件
     * 匹配路由,然后更新页面 DOM
     */
    window.addEventListener('popstate', e => {
      const current = this.current

      // Avoiding first `popstate` event dispatched in some browsers but first
      // history route not updated since async guard at the same time.
      const location = getLocation(this.base)
      if (this.current === START && location === initLocation) {
        return
      }

      this.transitionTo(location, route => {
        if (supportsScroll) {
          handleScroll(router, route, current, true)
        }
      })
    })
  }

  go (n) {
    window.history.go(n)
  }

  push (location, onComplete, onAbort) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      // 使用 pushState 更新 url,不会导致浏览器发送请求,从而不会刷新页面
      pushState(cleanPath(this.base + route.fullPath))
      onComplete && onComplete(route)
    }, onAbort)
  }

  replace (location, onComplete, onAbort) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      // replaceState 跟 pushState 的区别在于,不会记录到历史栈
      replaceState(cleanPath(this.base + route.fullPath))
      onComplete && onComplete(route)
    }, onAbort)
  }
}

造个轮子~

我用纯 ES6 写了个前端的路由,没有用任何框架,就可以实现前端路由的所有功能~

当然,实现的思路就是上面的那两幅流程图。

在线演示地址

项目源码

你还可以通过 npm 来安装这个包(功能已经实现完整,并有详细文档)

npm i --save sme-router

觉得有帮助的,请点个 star ~~

后记

异步交互的最高级体验是什么?
PWA,让前端页面可以做到离线操作(是不是越来越像原生 app 了?)

觉得有帮助的话,请点个赞,点个 star ~~

希望看到更多干货的话,(●'◡'●) 快来关注我的杂货铺吧
?

查看原文

赞 89 收藏 228 评论 18

红蓝 赞了回答 · 2018-04-23

javascript总for of和for in的区别?

先说结论:

  1. 推荐在循环对象属性的时候,使用for...in,在遍历数组的时候的时候使用for...of

  2. for...in循环出的是key,for...of循环出的是value

  3. 注意,for...of是ES6新引入的特性。修复了ES5引入的for...in的不足

  4. for...of不能循环普通的对象,需要通过和Object.keys()搭配使用

假设我们要遍历一个数组的valuelet aArray = ['a',123,{a:'1',b:'2'}]

使用for...in循环:

for(let index in aArray){
    console.log(`${aArray[index]}`);
}

使用for...of循环:

for(var value of aArray){
    console.log(value);
}

咋一看好像好像只是写法不一样而已,那为什么说for...of修复了for...in的缺陷和不足。
假设我们往数组添加一个属性name:
aArray.name = 'demo',再分别查看上面写的两个循环:

for(let index in aArray){
    console.log(`${aArray[index]}`); //Notice!!aArray.name也被循环出来了
}
for(var value of aArray){
    console.log(value);
}

所以说,作用于数组的for-in循环除了遍历数组元素以外,还会遍历自定义属性。

for...of循环不会循环对象的key,只会循环出数组的value,因此for...of不能循环遍历普通对象,对普通对象的属性遍历推荐使用for...in

如果实在想用for...of来遍历普通对象的属性的话,可以通过和Object.keys()搭配使用,先获取对象的所有key的数组
然后遍历:

var student={
    name:'wujunchuan',
    age:22,
    locate:{
    country:'china',
    city:'xiamen',
    school:'XMUT'
    }
}
for(var key of Object.keys(student)){
    //使用Object.keys()方法获取对象key的数组
    console.log(key+": "+student[key]);
}

具体可以参考我这篇博客文章:JavaScript中的for-of循环

关注 28 回答 13

红蓝 赞了文章 · 2018-04-12

在 React 项目中使用 React-intl 实现多语言支持

封面图片来源:https://alpha.wallhaven.cc

最近在项目中添加了语言国际化的功能。

语言国际化,也有人说成是语言本地化,其实就是为Web App添加多语言,我们的项目当前包含了中文版和英文版,按理来说『逐字替换』也不是多大事儿,但是,这么Low的做法,有钱途吗?

一开始的时候,我考虑的是传统的为整个项目添加config文件,根据不同的语言和地区,加载不同的config文件,就能够达到界面语言切换的目的。当然,也正是因为这个想法太过于幼稚,所以才被称为『一开始』的想法。语言的国际化不仅仅是将界面上的UI文字翻译成另一种语言,还包括了日期&时间显示,数字显示(英文环境下每隔3位一个逗号:1,000),量词的显示(一个苹果是apple,两个苹果就应该是apples),甚至还有一个字符串中间插了一个变量的情况("今天午饭吃了{count}个鸡腿")...

图片描述

所以这并不只是一个简单的字符替换问题,并且,我们要很方便的导出一个目录,放到word或者page当中,给到其他同事对照着进行翻译工作,这个非常重要!!难道你要让产品经理直接在代码里改么?或者你想一个一个搜索替换?不考虑清楚就干的话,相信我,You'll pay for this。

作为一个有追求的代码家,你肯定不希望在index.html当中增加一行<Script>引用吧?另外,UI中的文字全部都是使用图片的那个同学,请起立,滚。如果想要在一个React项目中,优雅的import something from somewhere,然后将界面中的文字用<首字母大写 /> 组件替代,最后通过简单的配置实现语言的国际化,那我们就用React-intl吧。

注意:本文说的是用法,源码我也没有拜读过,太深的东西去github给作者留言吧。

图片描述

React-intl

项目地址:https://github.com/yahoo/reac...

React-intl是雅虎的语言国际化开源项目FormatJS的一部分,通过其提供的组件和API可以与ReactJS绑定。上面这句话援引了官方文档的说辞,主要表达的是,这是一个很屌的开源项目,有大团队支持,使用量也很大,不会太坑爹,你们放心用。虽然雅虎都快被收购了。

React-intl提供了两种使用方法,一种是引用React组建,另一种是直接调取API,官方更加推荐在React项目中使用前者,只有在无法使用React组件的地方,才应该调用框架提供的API,事实上,我在项目的过程中真的遇到了无法使用组件的情况,这个我会另外写一篇文章来描述。

React-intl提供的React组件有如下几种:

<IntlProvider /> 包裹在需要语言国际化的组建的最外层,为包含在其中的所有组建提供包含id和字符串的键值对。(如:"homepage.title":"Hommily";

日期时间

a. <FormattedDate /> 用于格式化日期,能够将一个时间戳格式化成不同语言中的日期格式。

传入时间戳作为参数:

<FormattedDate 
    value={new Date(1459832991883)}
/>

输出结果:

<span>4/5/2016</span>

b. <FormattedTime> 用于格式化时间,效果与<FormattedDate />相似。

传入时间戳作为参数:

<FormattedTime 
   value={new Date(1459832991883)}
/>

输出结果:

<span>1:09 AM</span>

c. <FormattedRelative /> 通过这个组件可以显示传入组件的某个时间戳和当前时间的关系,比如 “ 10 minutes ago"。

传入时间戳作为参数:

<FormattedRelative 
    value={Date.now()}
/>

输出结果:

<span>now</span>

10秒之后的输出结果:

<span>10 seconds ago</span>

1分钟之后的输出结果:

<span>1 minute ago</span>

数字量词

a. <FormattedNumber /> 这个组件最主要的用途是用来给一串数字标逗号,比如10000这个数字,在中文的语言环境中应该是1,0000,是每隔4位加一个逗号,而在英语的环境中是10,000,每隔3位加一个逗号。

传入数字作为参数:

<FormattedNumber 
    value={1000}
/>

输出结果:

<span>1,000</span>

b. <FormattedPlural /> 这个组件可用于格式化量词,在中文的语境中,其实不太会用得到,比如我们说一个鸡腿,那么量词就是‘个’,我们说两个鸡腿,量词还是‘个’,不会发生变化。但是在英文的语言环境中,描述一个苹果的时候,量词是apple,当苹果数量为两个时,就会变成apples,这个组件的作用就在于此。

传入组件的参数中,value为数量,其他的为不同数量时对应的量词,在下面的例子中,一个的时候量词为message,两个的时候量词为messages。实际上可以传入组件的量词包括 zero, one, two, few, many, other 已经涵盖了所有的情况。

<FormattedPlural
    value={10}
    one='message'
    other='messages'/>

传入组件的量词参数可以是一个字符串,也可以是一个组件,我们可以选择传入<FormattedMessage />组件,就可以实现量词的不同语言的切换。

输出结果:

<span>messages</span>

字符串的格式化

a. <FormattedMessage /> 这个组件用于格式化字符串,是所有的组件中使用频率最高的组件,因为基本上,UI上面的每一个字符串都应该用这个组件替代。这个组件的功能丰富,除了可以根据配置输出不同语言的简单字符串之外,还可以输出包含动态变化的参数的复杂字符串,具体的用法在后面的例子中会慢慢叙述。

比如我们在locale配置文件中写了如下内容:

const app = {
    greeting:'Hello Howard!",
}

export default app;

使用这个组件的时候,我们这么写:

<FormattedMessage
    id='app.greeting'
    description='say hello to Howard'
    defaultMessage='Hello, Howard!'
    />

id指代的是这个字符串在locale配置文件中的属性名,description指的是对于这个位置替代的字符串的描述,便于维护代码,不写的话也不会影响输出的结果,当在locale配置文件中没有找到这个id的时候,输出的结果就是defaultMessage的值。

输出的结果:

<span>Hello, Howard!</span>

b. <FormattedHTMLMessage /> 这个组件的用法和<FormattedMessage />完全相同,唯一的不同就是输出的字符串可以包含HTML标签,但是官方不太推荐使用这个方法,如果可以想办法用<FormattedMessage />的话,就不应该使用这个组件,我揣测应该是性能方面不如<FormattedMessage />,这个组件的用法我就不举例了。

Well,到此为止,已经把React-intl提供的所有组件介绍完了,下面就给大家介绍一下具体如何去使用吧。

React-intl 使用步骤

(本文例子运行在OSX环境,Window操作方法的终端在安装的时候要注意用管理员身份运行)

1. 安装React-intl

假设你已经在你的系统中安装了node.js和npm,如果你还不知道这两个是什么东西,请自行百度,对,在百度都能找到答案。

打开终端,进入项目根目录,输入以下指令安装React-intl:

npm install react-intl -save

注意:为了兼容Safari各个版本,需要同时安装 intl,intl在大部分的『现代』浏览器中是默认自带的,但是Safari和IE11以下的版本就没有了,这里需要留个心眼。

安装intl需要在终端中输入以下指令:

npm install intl --save

这里还有一个注意:由于React-intl的每一个组件的使用方法大同小异,和ReactJS的语法完全一致,所以我就仅仅描述如何使用<FormattedMessage />这个组件的用法,借此抛砖引玉,相信看完之后已经足够帮助你迅速的去使用这个开源框架了。

2. 引用React-intl

import { FormattedMessage } from 'react-intl'; 

由于我使用的是ES6 的语法,所以是支持直接引用组件的。你当然可以使用ES5的方式引用,但是,这样有前途么?

require ReactIntl from 'react-intl';

3. 创建locale配置文件

这里,我们将文件命名为zh_CN.js和en_US.js,代表中文和美式英语的配置包。

在zh_CN.js编写如下代码:

const zh_CN = {
            hello:"你好,方浩!",
            superHello:"你好,{ someone } !"
        }
export default zh_CN;    

在en_US.js编写如下代码:

const en_US = {
            hello:"Hello, Howard!",
            superHello:"Hello, { someone } !"
        }    
export default en_US;

于是,我们就创建好了locale文件,但是,在实际的项目中配置文件不会这么简单,您可能需要根据业务需求按照不同的页面或者不同的功能块创建不同的文件树,然后用模块化的方法将不同的配置文件进行组织,已达成你的目标,这里我也就没能力逼逼太多了。

你可能想问,{ someone } 是什么鬼?其实悟性高一些的话就应该已经猜到,这个应该就是前面提到过的在字符串中插入动态参数的用法,事实上也是这样的。

4. 使用<IntlProvider />

使用<IntlProvider /> 组件包裹住需要您需要进行语言国际化的组件,用法和React-redux的<Provider />差不多,当<IntlProvider /> 包裹住某个组件的时候,这个组件本身和组件内部包含的子组件就可以获得所有React-intl提供的接口以及在<IntlProvider /> 中引入的locale配置文件的内容。

import React from 'react';
import { render } from 'react-dom';
//引入locale配置文件,具体路径根据实际情况填写
import zh_CN from './zh_CN';
import en-US from './en-US';
//如果浏览器没有自带intl,则需要在使用npm安装intl之后添加如下代码
import intl from 'intl';
addLocaleDate([...en,...zh]);

...
...

render(    
        <IntlProvider 
            locale={'en'} 
            messages={en_US}
        >
            <App />
        </IntlProvider>,    
        document.getElementById('container')
);

<IntlProvider />需要传递两个参数:

locale是传递需要国际化的语言的缩写,通过这个参数可以确定格式化日期,数字,量词的时候按照哪一种语言的规则,这个是规则是intl提供的,一般浏览器会内置这个库,但是在Safari和IE11之前需要自己安装,安装的方法前面已经提及,请自己翻阅。

messages是用于传递刚刚我们在第3步中定义的配置文件的,从示例代码中我们可以看出,首先我们使用Import语句引入了配置文件,然后将配置文件的内容传递给了messages这个参数,此时<App />组件中的所有组件都可以拿到配置文件中的内容了。

那个跳起来的同学,请先坐下,我猜你是想问,是不是每次都要手动修改这两个参数以适配不同语言呢?

其实不然,我们完全可以按照下面的做法自动识别当前浏览器的语言:

chooseLocale(){
    switch(navigator.language.split('_')[0]){
        case 'en':
            return 'en_US';
            break;
        case 'zh':
            return 'zh_CN';
            break;
        ...
        ...
        ...
        default:
            return 'en_US';
            break;
    }
}
render(    
        <IntlProvider 
            locale={navigator.language} 
            messages={chooseLocale()}
            >
            <App />
        </IntlProvider>,    
        document.getElementById('container')
);

您还需要知道的是,<IntlProvider />是可以嵌套使用的,也就是说,在一个<IntlProvider />内部还可以有N个<IntlProvider />,这个功能的实际意义就是可以在英文网站中嵌套一个中文的或者德语的或者法语的板块,应用起来会更加灵活一些。

5. 使用<FormattedMessage />

前面的几个步骤其实都是为了这个步骤做铺垫的,在添加了<IntlProvoder />之后,我们就可以在其包裹的<App />及<App />包含的所有组件中获取到配置文件的信息,传入<FormattedMessage />组件的id参数也能其在配置文件中对应的字符串了。

使用的方法如下:

<FormattedMessage
    id='hello'
    description='say hello to Howard.'
    defaultMessage='Hello, Howard'
    />

在Js执行的时候,组件就会找到配置文件中,‘hello'键名对应的字符串'Hello, Howard!'.

输出的结果为:

<span>Hello, Howard!</span>

那么如何输出含有动态参数的字符串呢?比如Hello,Johnson!,如果我要问候的对象是一个变量呢?

那就这么写呗。。

<FormattedMessage
    id='superHello'
    description='say hello to Howard.'
    defaultMessage='Hello, {someone}'
    values={
        someone:this.props.name,
    }
    />

以上的例子中,赋给someone的就是一个变量(假设这个变量是通过参数传进这个组件的),注意,如果是这样的话,那么locale配置文件中就要这么写。

 superHello:"你好,{ someone } !"

前面其实提过了,怕你忘了...我已经悄无声息的把id换成了superHello。

更牛逼的是,这个someone还可以包含HTML标签!

<FormattedMessage
    id='superHello'
    description='say hello to Howard.'
    defaultMessage='Hello, {someone}'
    values={
        someone:<b>this.props.name</b>,
    }
    />

输出结果:

<span>Hello, <b>Howard</b>!</span>

于是,这个名字就被加粗了。

眼尖的同学又要跳起来了,“webFunc,为什么所有的输出都带一个<span>标签,我就不能换成别的么?”

不要着急,我正要说这个,对于这个问题,官方的文档是这么说的。

By default <formattedMessage> will render the formatted string into
a <span>. If you need to customize rendering, you can either wrap it
with another React element (recommended), specify a different tagName
(e.g., 'div'), or pass a function as the child.

翻译过来就是,默认的是会包裹在<span>标签中的,如果想要让输出的字符串包裹在其他标签中的话,比如你想包裹在<div>中,你就把<FormattedMessage />组件包含在一对<div>中间,这是一种官方更加推荐的做法。

<div>
<FormattedMessage
    id='hello'
    description='say hello to Howard.'
    defaultMessage='Hello, Howard!"
    />
</div>

Well, that's stupid...

或者你可以给<FormattedMessage>传入一个tagName的参数。比如:

<FormattedMessage
    id='hello'
    tagName = 'div'
    description='say hello to Howard.'
    defaultMessage='Hello, Howard!'
    />

就会输出:

<div>Hello, Howard!</div>

比较奇葩的是,也是我揣测作者不推荐使用这种方法的原因是...只要你高兴,tagName可以传入任意字符串,比如 shit:

<FormattedMessage
    id='hello'
    tagName = 'shit'
    description='say hello to Howard.'
    defaultMessage='Hello, Howard!'
    />

就会输出:

<shit>Hello, Howard!</shit>

Yes, shit happens.

看到这里,你应该已经会使用React-intl对你的项目进行语言国际化了,没有进一步描述的地方,请自行查阅官方文档(项目地址:https://github.com/yahoo/reac...,或者给我留言,虽然我不一定会及时回复。

--写在后面:

语言国际化应该是一个比较经常遇到的需求,但是我在完成项目的过程中,看到的中文的资料却相当少,虽然这不是一篇非常牛叉的技术文章,但是可能会帮到很多人,如若如此,也便满足了。
——方浩(webFunc)

对了,你可以关注一下我的微信公众号:webcoding

图片描述

查看原文

赞 43 收藏 87 评论 35

认证与成就

  • 获得 1 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-04-16
个人主页被 90 人浏览