1. 程式人生 > >Weex學習與實踐(一):Weex,你需要知道的事

Weex學習與實踐(一):Weex,你需要知道的事

本文主要介紹包括Weex基本介紹、Weex原始碼結構、初始化工程、we程式碼結構、Weex的生命週期、Weex的工作原理、頁面間通訊、boxmodel & flexbox、weex的缺點

基本介紹

A framework for building Mobile cross-platform UI

怎麼解釋它呢?我的理解就是weex = react-native +vue ,使用vue的API風格,兩端的實現方式則和react-native,weex 比rn的優點就是一次編寫三端執行。

weex分為元件component和模組(module)以及事件

元件 就是各種標記元件,比如div 、slider、indicator等 通過下面這種方式使用

<div>
  <image src="..."></image>
  <text>...</text>
</div>

js模組


let modal = require('@weex-module/modal');
modal.toast({
  "message":"我是提示框",
  "duration":2
});  

其他的還有stream,dom,animation之類的

事件

  <div onviewappear="viewappear" onviewdisappear="viewdisappear">
          ......
  </div>

Weex原始碼結構

package.json

node_modules依賴,更重要的是裡面包含了npm run xxx 等快捷命令。比如之前我們執行node.js程式是這樣的:$ node xx.js。這裡我們可以把它配置化,例如package.json檔案中scripts的 “build:config”: “node build/config.frameworks.js”,其實就是npm run build:config 相當於執行了node build/config.frameworks.js

start檔案: 啟動程式檔案,裡面包換編譯和啟動指令碼:

examples: 示例Demo

android/ios/html: 各平臺程式碼

build:打包各平臺的指令碼,配置在package.json中。

初始化工程

初始化工程前需要先安裝 homebrew,然後按照下面步驟建立一個工程。


$ brew install node             //通過brew安裝node
$ npm install -g weex-toolkit   //通過node安裝 weex-toolkit  
$ sudo gem install cocoapods    //安裝iOS包管理工具 cocoapods
$ weex init                     //建立專案的檔案
$ npm install                   //依賴安裝 package.json檔案
$ npm run dev                   //專案編譯
$ npm run serve                 //啟動輕量伺服器  

這時有可能提示

npm WARN [email protected] requires a peer of [email protected]^6.0.0 but none was installed.

你需要再

npm install babel-core

這時,開啟瀏覽器,輸入http://127.0.0.1:8080, 就會看到這個專案的效果:

npm run dev 幹了什麼呢?

先看 package.json 檔案

{
  "name": "demo1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack",
    "dev": "webpack --watch",
    "serve": "serve -p 8080",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "serve": "^1.4.0",
    "webpack": "^1.13.1",
    "weex-html5": "0.2.18",
    "weex-loader": "^0.1.5"
  }
}

npm run dev實際上相當於 webpack –watch

webpack實際上是執行了預設的webpack.config.js配置檔案

webpack.config.js 引入webpack和weex-loader,entry屬性是表示入口檔案,output表示輸出檔案,預設輸出到dist資料夾。

require('webpack')
require('weex-loader')

var path = require('path')

module.exports = {
  entry: {
    main: path.join(__dirname, 'src', 'main.we?entry=true')
  },
  output: {
    path: 'dist',
    filename: '[name].js'
  },
  module: {
    loaders: [
      {
        test: /\.we(\?[^?]+)?$/,
        loaders: ['weex-loader']
      }
    ]
  }
}

不過這個自動產生的webpack.config.js的檔案有個坑就是,你新增一個新的we檔案,他不會自動build為js檔案 可以手動新增

  entry: {
    main: path.join(__dirname, 'src', 'main.we?entry=true'),
    translate: path.join(__dirname, 'src', 'translate.we?entry=true')
  }

不過推薦的是自己遍歷所有的we檔案

require('webpack')
require('weex-loader')

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

var entry = {};

function walk(dir, root) {
  var directory = path.join(__dirname, root, dir);
  fs.readdirSync(directory)
    .forEach(function(file) {
      var fullpath = path.join(directory, file);
      var stat = fs.statSync(fullpath);
      var extname = path.extname(fullpath);
      if (stat.isFile() &&
             (extname === '.we')) {
        var name = path.join(root, 'build', dir, path.basename(file, extname));
        entry[name] = fullpath + '?entry=true';
      } else if (stat.isDirectory() &&
                  file !== 'build') {
        var subdir = path.join(dir, file);
        walk(subdir, root);
      }
    });
}
walk('./', 'src');
module.exports = {
  entry: entry,
  output: {
    path: '.',
    filename: '[name].js'
  },
  module: {
    loaders: [
      {
        test: /\.we(\?[^?]+)?$/,
        loaders: ['weex-loader']
      }
    ]
  }
}

入口檔案index.html

weex程式碼結構

template內必須包含唯一的根節點作為父容器, div就是一個很好的選擇,裡面則是一些Native Components

style 支援盒子模型和Flexbox

weex內建了響應式的支援,頁面的寬度是以750來做為標準,自動適配所有手機;


<template>
  <div>
    <div>子元件</div>
    <div>子元件</div>
  </div>
</template>

<style>
</style>
<script>
module.exports = {

  data: function () {
    return {
      x: 1,
      y: 2
    }
  }

  methods: {
    foo: function () {
      console.log('foo')
    }
  },

  computed: {
    z: function () {
      return this.x + this.y
    }
  },

  events: {
    custom: function (e) {
      console.log(e)
    }
  },

  init: function () {},
  created: function () {},
  ready: function () {}
}

</script>

script裡面包含很多ViewModel Options,

data methods computed init, created, ready events

如果需要在模板裡實現更多的邏輯判斷,你可以使用’computed property’.

created是生命週期函式,這個時候模板還沒有被渲染,常用來在這裡定義資料的更新和獲取;

ready是生命週期函式,這個時候模板被渲染,常用來做一些自己上報等;

顯然we檔案的這些程式碼是不會被 native app 識別的,我們要想辦法讓這些程式碼可執行。所以我們同時做了三件事:

1.在本地用一個叫做 transformer 的工具把這套程式碼轉成純 JavaScript 程式碼

2.在客戶端執行一個 JavaScript 引擎,隨時接收 JavaScript 程式碼

3.在客戶端設計一套 JS Bridge,讓 native 程式碼可以和 JavaScript 引擎相互通訊

所以緊接著第二步,就是用 transformer 對程式碼進行轉換,變成客戶端可執行的 JavaScript 程式碼

原圖:本地開發時的 Weex Transformer 工作原理

在 transformer 中,我們主要的工作就是對 HTML、CSS、JavaScript 程式碼進行解析和重組。這裡我們用到了三個非常重要的庫:

CSS 解析工具:cssom

JavaScript 解析工具:uglify-js

Weex 的生命週期

<script>
  module.exports = {
    data: {},
    methods: {},

    init: function () {
      console.log('在初始化內部變數,並且添加了事件功能後被觸發');
    },
    created: function () {
      console.log('完成資料繫結之後,模板編譯之前被觸發');
    },
    ready: function () {
      console.log('模板已經編譯並且生成了 Virtual DOM 之後被觸發');
    },
    destroyed: function () {
      console.log('在頁面被銷燬時呼叫');
    }
  }
</script>

init內一般用於初始化一些內部變數,繫結一些自定義事件,這時還沒有資料繫結,沒有建立vdom,所以不能通過this獲取到data和methods,也不能獲取vdom的節點

created 完成了資料繫結 ,但還未開始編譯模板,可以通過this獲取data和methods,但不能獲取vdom的節點

ready表示渲染完成 ,從子元件往上觸發

destroyed 元件銷燬,比如頁面跳轉,從子元件開始往上觸發

Weex的工作原理

頁面間通訊

頁面跳轉是通過指定下一個頁面的url,然後通過openurl或者push的方式來跳轉

獲取url的方式可以通過下面這段JS程式碼

function getAppBaseUrl(self) {
    var dir ='examples'
    var url = self.$getConfig().bundleUrl;
    var bundleUrl = url;
    bundleUrl = new String(bundleUrl);

    var nativeBase;
    var isAndroidAssets = bundleUrl.indexOf('file://assets/') >= 0;

    var isiOSAssets = bundleUrl.indexOf('file:///') >= 0;
    if (isAndroidAssets) {
      nativeBase = 'file://assets/';
    }
    else if (isiOSAssets) {
      nativeBase = bundleUrl.substring(0, bundleUrl.lastIndexOf('/') + 1);
    }
    else {
      var host = 'localhost:12580';
      var matches = /\/\/([^\/]+?)\//.exec(self.$getConfig().bundleUrl);
      if (matches && matches.length >= 2) {
        host = matches[1];
      }
      nativeBase = 'http://' + host + '/' + dir + '/build/';
    }
    var h5Base = './index.html?page=./' + dir + '/build/';
    //Native端
    var base = nativeBase;
    //H5端
    if (typeof window === 'object') {
      base = h5Base;
    }
    return base
}

頁面通訊有兩種方式

1.通過 url 引數傳遞。

/**
 * 獲取URL引數
 */
getUrlParam: function (key) {
    var t = this.$getConfig().bundleUrl;
    var reg = new RegExp('[?|&]' + key + '=([^&]+)');
    var match = t.match(reg);
    return match && match[1];
}

2.通過 localStorage 資料儲存。

boxmodel & flexbox

weex支援boxmodel 和flexbox

下面這個是boxmodel

關於flexbox,可以看我的這篇文章[react-native的第一課

  • flexbox佈局](http://coderyi.com/posts/react-native_first_lesson/#flexbox佈局)

weex的缺點

1.Weex將整個app的寬度定死在750px,然後其他都是根據scale進行計算的,會導致適配不方便。

2.目前不支援iOS的presentViewController方法

3.很多元件和模組需要自己擴充套件(比如datepicker,iconfont,攝像頭,二維碼等)

幾個小問題

2.怎麼斷點除錯?

目前是可以斷點除錯的,可以參考下面文章

線上除錯

3.weex支援本地圖片嗎?

根據官方答疑是可以的,但是我目前還沒有嘗試成功。

5.promise怎麼使用?

6.熱更新方案是什麼?

最後,希望有越來越多的人把weex用起來。