基于vue2.0的weex实践(前端视角)

19年目标:消灭英语!我新开了一个公众号记录一个程序员学英语的历程css

有提高英语诉求的小伙伴能够关注公众号:csenglish 程序员学英语,天天花10分钟交做业,跟我一块儿学英语吧html


前提:这段时间将公司的几个用we写的weex页面用vue2.0进行了重构,客户端的weexsdk也更新到了0.10.0版本。这篇文章将会在前端视角写到一些新老版本之间有差别的地方。we老版本的实践文章前端

weex-vue-render v 0.10.0vue

ios weexSDK 0.10.0html5

android weexSDK 0.10.0android

编译vue文件

在we时代,对于业务代码text.we文件咱们借助weex-loader以及webpack能够轻易的进行编译,获得最后须要的js。可是在vue时代,咱们须要两个loader,weex-loadervue-loader,对于公司接入来讲咱们不太会使用官方提供的脚手架工具,通常都是本身实现。这里容易产生混淆的是还有一个loader叫作weex-vue-loader,这个loader通常状况下对于开发者而言不须要手动调用。webpack

在build阶段或者在dev阶段,咱们会使用weex-loadervue-loader两个loader。ios

// native端使用的js

weexWebpackConfig.module.loaders.push({
  test: /\.vue(\?[^?]+)?$/,
  loader: require.resolve('weex-loader')
})
weexWebpackConfig.output.filename = '[name].weex.min.js'
复制代码
// h5端使用的js

vueWebpackConfig.module.loaders.push({
  test: /\.vue(\?[^?]+)?$/,
  loader: require.resolve('vue-loader')
})
vueWebpackConfig.output.filename = '[name].min.js'
复制代码

对于native端咱们使用weex-loader进行编译vue文件,在weex-loader内部若是检测到该文件是vue则调用weex-vue-loader。对于H5端咱们则使用vue官方提供的vue-loader进行vue的编译,同时咱们对编译完成的文件名作区别,方便识别。git

对于你们本身实现脚手架很容易形成问题的一点是咱们须要加入一段注释,让weexsdk识别这是vue。程序员

var bannerPlugin = new webpackG.BannerPlugin(
    '// { "framework": "Vue" }\n',
    { raw: true }
  )
webpackConfig.plugins.push(bannerPlugin);
复制代码

来源:官方文档

源码依赖管理

咱们的业务是多页形式的,因此接入weex也很是方便。在每一个页面引入该页面的由vue编译完后js文件。vueweex-vue-render做为公共资源咱们统一引入,由于咱们须要结合native方定制一些错误收集等module,因此咱们选择将vue、weex-vue-render以及定制化的module、component共同做为源码管理,统一打包成一个公共的文件在H5端引用。

we时代咱们也是这么作的,详见。vue时代在大致方向不变,在入口文件处作了微小的调整。

import Vue from 'vue'
window.Vue = Vue

/**
 * 注册component
 * 
 */

// import TestComponent from './components/test/test.vue'
// Vue.component('test-component', TestComponent)


/**
 * 注册module
 * 
 */
require('weex-vue-render')

// shopBase
import shopBase from './modules/shop-base'
window.weex.registerModule('shopBase', shopBase)

// shopModal
import shopModal from './modules/shop-modal'
window.weex.registerModule('shopModal', shopModal)
复制代码

咱们能够看到相比之前component和module都由weex进行注册,而如今vue时代,咱们的component将由Vue进行注册,须要注意的是这里我将Vue挂载到了window对象下,由于在各自页面的业务代码中,咱们的入口js中须要实例化Vue,须要用到Vue对象。

以及在注册module的时候咱们的weex也是取自window对象下,require('weex-vue-render')这个步骤中会将weex挂载在window之下。

具体的component编写如今已经完成变成了vue文件的编写,有vue经验的同窗1秒上手。

具体的module编写相比较之前也简化了不少,export一个包含module方法的对象,再调用weex.registerModule进行注册便可。值得注意的是在新官方文档上对html5的拓展部分没有讲到如何书写回调,回调的规则跟旧版仍是同样的,下面的代码片断就是例子

const shopBase = {
  isOnline: function (callbackId) {
    const sender = this.sender;
    let hostname = window.location.hostname;
    let result = false;
    if (hostname.indexOf('showjoy.com') !== -1) {
      result = true;
    }
    sender.performCallback(callbackId, {
      data: result
    })
  }

}
export default shopBase


// another js

window.weex.registerModule('shopBase', shopBase)
复制代码

viewport的转变

这部份内容原理解释的有些绕,对viewport适配不了解的同窗能够先看些我以前的文章viewport-and-flexibleJs

开发过we页面的同窗应该了解,在以前的weex-hmtl5版本的weex中应该是没有对多屏幕视口适配作处理的,框架把视口适配的工做交给了手淘的flexible.js进行基于rem的视口适配。咱们在H5端写页面的时候须要引入一段flexible.js脚本。

基于vue2.0版本的weex舍弃了flexible.jsrem适配方案,改为了750px固定视口的方案。咱们会看到无论在什么尺寸的手机屏幕上,layout viewport的值都为750px,因此我编写css代码的时候是无异的,都是以750为满屏宽进行代码的编写,区别仅在于如今要写度量单位px,而we时代是不须要的。

我在重构we页面的时候经历了weex-vue-render两个版本的迭代,一个是v-0.2.0还有一个就是v-0.10.0

这两个版本对viewport的处理也作了修改,咱们在编写业务代码的时候也受到了一些由版本迭代带来的影响。

weex-vue-render v-0.2.0版本以及weex-html5老weex版本时咱们在html文件中是能够不须要写meta[name=viewport]的,框架会自动帮咱们计算获得页面须要的viewport信息。

weex-html5配合使用flexible适配多屏幕,这无需多言了。

weex-vue-render v-0.2.0的适配方式以下代码所示:

// 本文做者注释
// 此处默认值是750,由build时注入
const DEFAULT_VIEWPORT_WIDTH = process.env.VIEWPORT_WIDTH

function parseViewportWidth (config) {
  let width = DEFAULT_VIEWPORT_WIDTH
  if (config && config.width) {
    width = Number(config.width) || config.width
  }
  return width
}

export function setViewport (config = {}) {
  const doc = window.document

  if (doc) {
    const viewportWidth = parseViewportWidth(config)

    // set root font-size
    doc.documentElement.style.fontSize = viewportWidth / 10 + 'px'
  
  // 本文做者注释 
  // 重点语句 获得当前手机屏幕的宽度
    const screenWidth = window.screen.width 
    const scale = screenWidth / viewportWidth
    const contents = [
      `width=${viewportWidth}`,
      `initial-scale=${scale}`,
      `maximum-scale=${scale}`,
      `minimum-scale=${scale}`,
      `user-scalable=no`
    ]

    let meta = doc.querySelector('meta[name="viewport"]')
    if (!meta) {
      meta = doc.createElement('meta')
      meta.setAttribute('name', 'viewport')
      document.querySelector('head').appendChild(meta)
    }

    meta.setAttribute('content', contents.join(','))
  }
}
复制代码
const screenWidth = window.screen.width 
复制代码

这条语句是这个适配脚本的重点之一,本意是为了拿到当前手机屏幕的ideal viewport理想视口,多数状况下window.screen.width是能够拿到屏幕的ideal viewport的,可是少部分安卓原生浏览器取到的值是有问题的。在正常状况iphone6下,这段脚本获得的值为:

`width=750`,
`initial-scale=0.5`,
`maximum-scale=0.5`,
`minimum-scale=0.5`,
`user-scalable=no`
复制代码

可是在部分安卓手机下的浏览器会获得错误screenWidth从而获得错误的initial-scale,那么屏幕显示会出现问题。因此weex-vue-render v-0.10.0版本修改了这个脚本的实现,可是代价是咱们必需要在html文件中添加一段meta[name=viewport]标签。

标签的形式是:<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

须要添加这个标签没有在更新日志中体现,只在官网的example中修改了demo的实现,被这个坑绊到了。。。

如今咱们看一下在weex-vue-render v-0.10.0中是如何获取到设备的ideal viewport的,这也就解释了为什么要在html中添加这个标签了。

咱们直接看源码修改的部分,这里将window.screen.width修改为了document.documentElement.getBoundingClientRect().width

直白话讲就是取得当前屏幕的html的宽度,而html的宽度则是由<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">标签的width=device-width决定的,结合起来说就是这个标签将html的宽度设置成了device-width宽,而这个宽就是设备的ideal viewport的宽。并且这个值是永远正确的。这种方式计算获得的viewport在各类设备上都会以750px宽度呈现。

另外若是咱们是以源码依赖的方式而不是以script方式管理weex-vue-render的,0.10.0版本会有一个bug致使咱们获得的viewport的值是undefined。

缘由在于weex-vue-rendernpm包中package.json的main字段指向了"main": "src/render/vue/index.js",源码,而源码中的viewport相关代码中含有let viewportWidth = process.env.VIEWPORT_WIDTH环境变量,并无文档说明咱们在源码依赖weex-vue-render进行编译的时候须要给环境变量process.env.VIEWPORT_WIDTH设置750的值。因此编译出来的viewportWidth变量的值确定是undefined。

你们能够npm i weex-vue-render@0.10.0查看一下。

解决办法一则将main入口指向weex-vue-render编译好的文件。方法二能够在咱们本身管理源码的时候添加这个环境变量。

0.10.0sdk支持相对地址并自动补全

0.4.0版本的sdk中是不支持相对地址自动补全的,而咱们前端同窗在写业务逻辑时都是以相对地址的方式编写跳转路由或者api接口。

好比一个api请求

stream.fetch({
    method: 'GET',
    url: "/api/shop, type: 'json' }, function (response) {}) 复制代码

h5端浏览器会自动拼接上当前域名的host,而native端拿到的只是这个相对地址请求。我司native方的作法是在网络请求以前手动拼接上须要的host域名,完成网络请求。

可是native端的weexsdk从0.9.4版本开始支持不全相对地址。

Support relative URL, which will resolve real URL by bundle's URL.

可是比较坑的是与咱们的业务场景不符合。由于sdk自动补全的host是根据js bundle的url进行的。

好比咱们的js bundle地址是cdn1.xxx.com/js/weex.js,可是业务里须要请求的接口的地址是shop.m.xxx.com/api/getContent

问题就是咱们在.vue页面里写接口都是相对地址/api/getContent,这样的话weexSDK会根据bundle的host进行转换相对地址,变成了cdn1.xxx.com/api/getContent,致使接口请求404。

因此这个时候须要native那边去写各自的handler和adapter,重写各自的网络请求,再在weexSDK中进行注册,这意味着须要在sdk进行不全以前native就先把url进行补全了,不把机会留给sdk进行处理。

这个问题的issue

小结

这还只是初步迁移到vue时代的尝试,可是基础已经打好,更多的业务已经能够接入进来了。将来还有许多在H5中考虑的内容须要从新在weex的视角下再进行考虑。

本文来自南洋,有什么须要讨论的欢迎找我,尤为是viewport相关的内容。

相关文章
相关标签/搜索