18 Dec 2017

Weex基于Vue2.0开发模板搭建

简介:
Weex 是一套简单易用的跨平台开发方案,能以 web 的开发体验构建高性能、可扩展的 native 应用,使用 Vue 作为上层框架,打造三端一致的 native 应用
BUI-Weex 是一套专门为 Weex 前端开发者打造的一套高质量UI框架

参考
weex
BUI-Weex
Weex 插件

(一)weex 开发环境搭建

1. 安装工具

  1. 安装 Node.js
  2. 安装开发工具
    npm install -g weex-toolkit
    npm install -g webpack
    npm install -g serve
    # weex init projectDemo
     #npm install 
    # npm run dev 和 npm run serve
    # 浏览器: http://localhost:8080/index.html
    
  3. Android环境搭建
    3.1 JDK的变量环境
    右击“我的电脑”->“属性”->“高级系统设置”->“系统属性”->“高级”->“环境变量”
    创建 JAVA_HOME:C:\Java\jdk1.8.0_20
    编辑 Path:;%JAVA_HOME%\bin; CLASSPATH :.%JAVA_HOME%\lib\tools.jar   3.2 下载安装Android SDK
    1. 单独下载Android SDK
    2. 双击“SDK Manager.exe”
    在Tools下的 Options 里面,有一项 Force https://..sources to be fetched using http://... 将这一项勾选上,就可以了
    mirrors.neusoft.edu.cn 端口:80
    android-mirror.bugly.qq.com 端口:8080
    3. 再打开Android SDK Manager.exe
    (1)添加 ANDROID_HOME:E:\AndroidDevelopTool\android-sdk-windows
    (2)添加 path:%ANDROID_HOME%\tools;%ANDROID_HOME%\platform-tools
    4.验证:输入android -hadb命令

2. 初始化项目

npm install -g weexpack
weexpack create appName&&cd appName && npm install
weexpack platform add android
weexpack platform add ios

android.config.json 或者 ios.config.json 中指定的weex bundle文件 WeexBundle, 改为你的起始文件:如 index.js / main.js

3. 打包apk

打包:weexpack run android
(开启模拟器,夜神安装目录:nox_adb.exe connect 127.0.0.1:62001,Android SDK/platform-tools目录:adb connect 127.0.0.1:62001)
1) 下载gradle-2.14.1-all.zip复制到appName\platforms\android\gradle\wrapper目录
2)  gradle-wrapper.properties修改distributionUrl =gradle-2.14.1-all.zip
3) touch $ANDROID_HOME/licenses/android-sdk-license
8933bad161af4178b1185d1a37fbf41ea5269c55
4)修改 appName\platforms\android\build.gradle
5)更改项目目录下的android.config.json

AppName: 应用名
AppId: application_id 包名
SplashText: 欢迎页上面的文字
WeexBundle: 指定的weex bundle文件(支持文件名和url的形式)

4. 真机调试

  1. 设置/设定,打开开发者选项,勾上USB调试允许USB线调试
    weexpack run ios 构建ipa包 weexpack build ios
    打包运行:weexpack build web&&weexpack run web

1)netstat -aon|findstr “49157” 端口占用
2)端口号 默认为8080

  1. 运行:npm run serve
    1)修改端口号 webpack.dev.js
    config.devServer = {
      historyApiFallback: true,
      // progress:true,
      inline:true,
      contentBase: pathTo.join(__dirname, ''),
      compress: true,
      hot: true,
      host: '0.0.0.0',
      port:'8082',
      public: ip + ':8082/web'
     // publicPath: '/dist/',
    };
    
  2. 监控文件变化:webpack --watch
  3. 其他
    touch .babelrc
    { "presets": ["es2015"] }
    

    touch .gitignore

    .idea/
    node_modules/
    temp/
    dist/
    
  4. Webpack.config.js 修改js主文件,如果同文件名的文件js文件 就复制不是拷贝,同时限制为第一级目录生成js文件其他的不生成
    function walk(dir) {
     dir = dir || '.';
     const directory = pathTo.join(__dirname, 'src', dir);
     fs.readdirSync(directory)
         .forEach((file) => {
             const fullpath = pathTo.join(directory, file);
             const stat = fs.statSync(fullpath);
             const extname = pathTo.extname(fullpath);
             const name = pathTo.join(dir, pathTo.basename(file, extname));
             if (stat.isFile() && extname !== '.vue' && extname !== '.we') {
                 entry[name] = fullpath + '?entry=true';
                 weexEntry[name] = fullpath + '?entry=true';
                 return;
             }
             if (directory.indexOf("\\src\\") > 0) {
                 return;
             }
             if (stat.isFile() && extname === '.vue' || extname === '.we') {
                 if (!fileType) {
                     fileType = extname;
                 }
                 if (fileType && extname !== fileType) {
                     console.log('Error: This is not a good practice when you use ".we" and ".vue" togither!');
                 }
                 if (extname === '.vue') {
                     let entryFile = pathTo.join(vueWebTemp, dir, pathTo.basename(file, extname) + '.js');
                     fs.exists(pathTo.join(__dirname, 'src', dir, name + '.js'), function(exists) { //异步
                         if (!exists) {
                             fs.outputFileSync(pathTo.join(entryFile), getEntryFileContent(entryFile, fullpath));
                         }else {
                             fs.copy(pathTo.join(__dirname, 'src', dir, name + '.js'), pathTo.join(entryFile), function(err) {
                                 if (err) return console.error(err)
                             });
                         }                        
    
                     })                   
                     if (!entry[name]) {
                         entry[name] = pathTo.join(__dirname, entryFile) + '?entry=true';
                         weexEntry[name] = fullpath + '?entry=true';
                     }
                 }
                    
             } else if (stat.isDirectory() && file !== 'build' && file !== 'include') {
                 const subdir = pathTo.join(dir, file);
                 walk(subdir);
             }
         });
    }
    

    使用别名

    const resolve={
         extensions: ['.js', '.vue', '.json'],
         alias: {
             // 'vue$': 'vue/dist/vue.esm.js',
             '@': pathTo.resolve(__dirname, './src')
         },
         modules: [
             pathTo.join(__dirname, './node_modules')
         ]
     }
    resolve:resolve 
    

(二)集成 vue-router

新建页面 mkdir src\views&& touch src\views\Hello.vue

<template>
<div class="wrapper" @click="jump('/')"><text>主页</text></div>
</template>

新建路由跳转方法 mkdir src\mixins&& touch src\mixins\index.js

export default {
    methods: {
        jump(to){
            // jump:function(to) {
            console.log('to=' + to);
            if (this.$router) {
                this.$router.push(to)
            }
        }
    }
}

新建路由文件 touch src/router.js

import Router from 'vue-router';
import Hello from '@/views/Hello.vue';
import index from '@/index.vue';
Vue.use(Router)
export default new Router({
  mode: 'abstract',
  routes: [
    { path: '/hello', component: Hello },
    { path: '/', redirect: '/index' }
  ]
})

新建入口文件 touch src/index.js

import App from './index.vue'
import router from './router'
import mixins from './mixins'
Vue.mixin(mixins)
var vm = new Vue(Vue.util.extend(
    { el: '#root',
    router,
    }, App))
router.push('/')

编辑主页(html) src/index.vue

<template>
    <div class="wrapper" @androidback="back">
        <image :src="logoUrl" class="logo"></image>
        <text class="title" @click="update">Hello </text>
        <text class="desc">开始干活吧!!!</text>
        <list class="list">
            <cell class="cell" @click="jump('/hello')">
                <text>hello</text>
            </cell>
            <cell class="cell" @click="jump('/')">
                <text>主页</text>
            </cell>
        </list>
        <router-view style="flex:1"></router-view>
    </div>
</template>
<script>
export default {
    data: {
        logoUrl: 'http://img1.vued.vanthink.cn/vued08aa73a9ab65dcbd360ec54659ada97c.png',
        target: 'World',
        count: 0
    },
    methods: {
        update: function(e) {
            this.count++;
            this.target = '美女 ' + this.count;
            console.log('target:', this.target)
        },
        back: function() {
            this.$router.back()
        }
    }
}
</script>
<style>
.wrapper {
    align-items: center;
    margin-top: 120px;
}

.title {
    padding-top: 40px;
    padding-bottom: 40px;
    font-size: 48px;
}

.logo {
    width: 360px;
    height: 156px;
}

.desc {
    padding-top: 20px;
    color: #888;
    font-size: 24px;
}
</style>

(三)集成vuex

touch src\vuexStore.js

import Vuex from 'vuex'
if (WXEnvironment.platform !== 'Web') {
  Vue.use(Vuex)
}
const vuexStore = new Vuex.Store({
    state: {
        dic: {}
    },
    mutations: {
        addDic(state, dics) {
           state.dic=Object.assign(state.dic, dics);           
        }
    },
    getters: {             
        getDic: (state) => (group) => {
             // return `${state.dic}`;
            return state.dic;
        }

    }
})
export {  vuexStore as    default};
// dic = store.state.dic[group],
// // store.commit("addDic",group);

修改 src/index.js

import App from './index.vue'
import router from './router'
import mixins from './mixins'
import store from './vuexStore'
import { sync } from 'vuex-router-sync'
sync(store, router)
Vue.mixin(mixins)
var vm = new Vue(Vue.util.extend(
    { el: '#root',
     store,
    router,
    }, App))

router.push('/')

修改 src/index.vue

 <cell class="cell" @click="jump('/vuexStore')">
        <text>vuex</text>
  </cell>

修改 src/router.js

import vuexStore from '@/views/vuexStore.vue'
//....
    { path: '/vuexStore', component: vuexStore },

touch src/views/vuexStore.vue

<template>
    <div class="wrapper" @androidback="back">
        <text class="title" @click="update">添加字典</text>
        <text class="desc" @click="getDic">获取字典:</text>
    </div>
</template>
<script>
export default {
    data() {
        return {
            target: 'World',
            count: 0,
            dic: {}
        }
    },
    methods: {
        update: function(e) {
            this.count++;
            this.target = '美女 ' + this.count;
            this.$store.commit("addDic", { 'code': this.count, 'lable': this.target });
        },
        back: function() {
            this.$router.back()
        },
        getDic: function() {
            this.dic = {
                'aa': this.$store.getters.getDic("parm"),
                'bb': this.dic = this.$store.state.dic
            };
        }
    }
}
</script>

安卓打包:
报错信息为:Observed package id ‘build-tools;19.0.0’ in inconsistent location
修改 sdk\build-tools\android4.4目录名为sdk\build-tools\19.0.0

(四)网络请求

编辑index.js

const stream = weex.requireModule('stream')
Vue.fetch = stream.fetch;
Vue.prototype.$fetch = stream.fetch;

编辑 index.vue

 Vue.fetch({
                method: 'GET',
                url: 'https://cnodejs.org/api/v1/topics',
                data: {},
                type: 'json',
                headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }
            }, function(res) {
                console.log(JSON.stringify(res));
            })

(五)过滤器

mkdir src\fliter&touch src\fliter\index.js

export function double (a) {
  return a*a
}

编辑 index.js

import * as filters from './filters'
Object.keys(filters).forEach(key => { 
Vue.filter(key, filters[key]) 
})

编辑index.vue(使用过滤器)

<text class="title">点击次数*2: 8 </text>

(六)多页面

touch src/main.vue

<template>
<div>这是main页
<text @click="gotoPage('main')">返回index页</text>
</div>
</template>
<script>
import mixins from './mixins'
Vue.mixin(mixins)
</script>

touch src/utils.js

export function getBaseURL(vm) {
    var bundleUrl = weex.config.bundleUrl;
    console.log(`eex.config=${JSON.stringify(weex.config)}`);
    var nativeBase;
    if (isAndroid()) {
        console.log('isAndroid');
        nativeBase = 'file://assets/dist/';
    } else if (isiOS()) {
        console.log('isiOS');
        // file:///var/mobile/Containers/Bundle/Application/{id}/WeexDemo.app/
        // file:///Users/{user}/Library/Developer/CoreSimulator/Devices/{id}/data/Containers/Bundle/Application/{id}/WeexDemo.app/
        nativeBase = bundleUrl.substring(0, bundleUrl.lastIndexOf('/') + 1);
    } else {
        console.log('web');
        var host = 'localhost:8080';
        var matches = /\/\/([^\/]+?)\//.exec(bundleUrl);
        if (matches && matches.length >= 2) {
            host = matches[1];
        }
        nativeBase = 'http://' + host + '/' + '/build/';
    }
    console.log('nativeBase=' + nativeBase);
    var h5Base = './weex.html?page=./dist/web/';
    // in Native
    var base = nativeBase;
    if (typeof window === 'object') {
        // in Browser or WebView
        base = h5Base;
    }
    console.log(`getPlatform()=${getPlatform()}`);
    console.log(`base=${base}`);
    return base
}

export function isAndroid() {
    if (getPlatform() == 'Android' || getPlatform() == 'android') {
        return true;
    } else {
        return false;
    }
}

export function isiOS() {
    console.log(getPlatform() == 'iOS');
    return getPlatform() == 'iOS';
}

export function isWeb() {
    console.log(getPlatform() == 'Web');
    return getPlatform() == 'Web';
}

//{"env":{"platform":"android","osVersion":"4.4.4","appVersion":"1.0","weexVersion":"0.9.5","deviceModel":"HUAWEI ALE-CL00","appName":"com.kunion.weex","deviceWidth":"720","deviceHeight":"1184","scale":"2.0"}}
//{"bundleUrl":"http://localhost:8080/weex.html?page=./dist/web/entry.js","platform":"Web","weexVersion":"0.10.0","userAgent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36","appName":"Netscape","appVersion":null,"osName":"Chrome","osVersion":"56.0.2924.87","deviceModel":"unknown","deviceWidth":750,"deviceHeight":1245}

export function getPlatform() {
    if (weex.config.env) {
        return weex.config.env.platform;
    }
    return weex.config.platform;
}

export function getScreenWidth() {
    return weex.config.deviceWidth;
}

export function getScreenHeight() {
    return weex.config.deviceHeight;
}

修改 mixins/index.js

import { getBaseURL, isAndroid } from '../utils';
const event = weex.requireModule('event');
const navigator = weex.requireModule('navigator')

export default {
    methods: {
        jump(to){
            // jump:function(to) {
            console.log('to=' + to);
            if (this.$router) {
                this.$router.push(to)
            }
        },
         gotoPage: function(page) {
            var url = getBaseURL(weex) + page + '.js';
            if (typeof window === 'object') {
                url = getBaseURL(weex) + page;
                // url = url.replace(/\.\/dist\/web\//, '/dist/').replace(/\/web\//, '');
                url = url.replace(/\.\/dist\/web\//, '').replace(/\/web\//, '');
            }
            url = url.replace(/\/weex\.html/, '\/index.html');
            // navigator.push({
            //   url: url,
            //   animated: "true"//页面压入时需要动画效果
            // }, event => { console.log(event); })
            event.openURL(url);
        }
    }
}

修改 index.vue

 <cell class="cell" @click="gotoPage('main')">
                <text>main页</text>
            </cell>

修改 index.html

 (function() {
        function getUrlParam(key) {
            var reg = new RegExp('[?|&]' + key + '=([^&]+)')
            var match = location.search.match(reg)
            return match && match[1]
        };
        var page = getUrlParam('page');
        var defaultPage = '/dist/index.js';
        if (page) {
            page.replace(/\.\/dist\/web\//, '/dist/')

            if (typeof window === 'object') {
                var extName = page.substr(page.length - 3, 3)
                if (extName !== '.js') {
                    page = '/dist/' + page + '.js'
                }
            }
        } else {
            page = defaultPage;
        }

        var bundle = document.createElement('script')
        // only for web
        bundle.src = page.replace(/\.js$/, '.web.js')
        document.body.appendChild(bundle)
})();

(七)Veux+storage多页面 state 共享

编辑 vuexStore.js

import Vuex from 'vuex'
// Vue.use(Vuex)
if (WXEnvironment.platform !== 'Web') {
  Vue.use(Vuex)
}
const storage = weex.requireModule('storage')
var state = {
   dic: {}
}
// 从storage里加载数据
storage.getItem('STORAGE_KEY', event => {
  if (event.result == "success" && event.data){
      // 这里可以使用extend等方法,这里仅举例说明
      var data = JSON.parse(event.data);
      state.dic = data.dic;
  }
})
// 存储plugin,存储感兴趣的数据,store里数据太多,没必要全持久化
const storagePlugin = store => {
  store.subscribe((mutation, {dic}) => {
    storage.setItem('STORAGE_KEY', JSON.stringify({dic}),event => {
      console.log('cache success');
    })
  })
}
export default new Vuex.Store({
    plugins:[storagePlugin], 
    state: state,
    mutations: {
        addDic(state, dics) {
           state.dic=Object.assign(state.dic, dics);           
        },
        addAllDic(state,stateObject){
            state=stateObject;
        }
    },
    getters: {             
        getDic: (state) => (group) => {
             // return `${state.dic}`;
            return state.dic;
        }

    }
})

touch main.js

import App from './main.vue'
// import router from './router'
import mixins from './mixins'
import store from './vuexStore'
// import { sync } from 'vuex-router-sync'

const stream = weex.requireModule('stream')
Vue.fetch = stream.fetch;
Vue.prototype.$fetch = stream.fetch;
import * as filters from './filters'
Object.keys(filters).forEach(key => { 
Vue.filter(key, filters[key]) 
})
// sync(store, router)
Vue.mixin(mixins)
var vm = new Vue(Vue.util.extend({
    el: '#root',
    store,
    // router,
}, App))
// router.push('/')

(八)常用内置组件

main.vue

<template>
    <div class="wrapper">
        <text style=' padding-top: 40px; padding-bottom: 40px; font-size: 48px;'>这是main页</text>
        <text @click="gotoPage('index')">返回index页</text>
        <text @click="getDic()">获取字典</text>
        <!--<image :src="logoUrl" resize="cover" class="background" @load="onload"></image>-->
        <slider class="slider" interval="3000" auto-play="true" infinite="true">
            <div class="frame" v-for="img in imageList">
                <image class="image" resize="cover" :src="img.src"></image>
            </div>
            <indicator class="indicator"></indicator>
        </slider>
        <div style="background-color:#1b90f7;margin:30px;border-radius:10px;flex-direction: row;height:40px;border-color:#1b90f7;border-width:1px">
            <text style="align-items: center;justify-content: center;flex:3;text-align:center;vertical-align:center">写点啥</text>
            <input style="align-items: center;justify-content: center;flex:9;padding-left:5px;text-align:center" type="text" placeholder="你是超人么?" return-key-type="default" class="input" @change="" @return="" @input="" />
        </div>
        <div class="example">
            <text class="label">逛逛呗</text>
            <switch checked="true" disabled="false" @change="onchange"></switch>
        </div>
        <text class="label">下拉加载</text>
        <scroller class="scroller">
            <refresh class="refresh" @refresh="onrefresh" @pullingdown="onpullingdown" :display="refreshing ? 'show' : 'hide'">
                <text class="indicator">Refreshing ...</text>
            </refresh>
            <div class="cell" v-for="num in lists">
                <div class="panel">
                    <text class="text"></text>
                </div>
            </div>
        </scroller>
    </div>
</template>
<script>
// import mixins from './mixins'
// Vue.mixin(mixins)
export default {
    data() {
        return {
            checked: false,
            logoUrl: 'https://img.alicdn.com/tps/TB1z.55OFXXXXcLXXXXXXXXXXXX-560-560.jpg',
            imageList: [
                { src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg' },
                { src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg' },
                { src: 'https://gd3.alicdn.com/bao/uploaded/i3/TB1x6hYLXXXXXazXVXXXXXXXXXX_!!0-item_pic.jpg' }
            ],
            refreshing: false,
            lists: [1, 2, 3, 4, 5]
        }
    },
    methods: {
        onchange(event) {
            console.log(`onchage, value: ${event.value}`)
            this.checked = event.value
        },

        onrefresh(event) {
            console.log('is refreshing')
            modal.toast({ message: 'refresh', duration: 1 })
            this.refreshing = true
            setTimeout(() => {
                this.refreshing = false
            }, 2000)
        },
        onpullingdown(event) {
            console.log('is onpulling down')
            modal.toast({ message: 'pulling down', duration: 1 })
        },
        getDic() {
            console.log(this.$store.state.dic);
        }
    },
    mounted: function() {},
    ready: function() {}

}
</script>
<style scoped>
.wrapper {
    align-items: center;
    margin-top: 120px;
}


.logo {
    width: 360px;
    height: 156px;
}

.desc {
    padding-top: 20px;
    color: #888;
    font-size: 24px;
}

.background {
    width: 1000px;
    height: 1000px;
}


.example {
    flex-direction: row;
    justify-content: flex-start;
    margin-top: 60px;
}

.label {
    font-size: 40px;
    line-height: 60px;
    width: 350px;
    color: #666;
    text-align: right;
    margin-right: 20px;
}

.info {
    font-size: 30px;
    line-height: 60px;
    color: #BBB;
    margin-left: 10px;
}

.image {
    width: 700px;
    height: 700px;
}

.slider {
    margin-top: 25px;
    margin-left: 25px;
    width: 700px;
    height: 700px;
    border-width: 2px;
    border-style: solid;
    border-color: #41B883;
}

.title {
    position: absolute;
    top: 20px;
    left: 20px;
    padding-left: 20px;
    width: 200px;
    color: #FFFFFF;
    font-size: 36px;
    line-height: 60px;
    background-color: rgba(0, 0, 0, 0.3);
}

.frame {
    width: 700px;
    height: 700px;
    position: relative;
}

.indicator {
    width: 700px;
    height: 700px;
    item-color: green;
    item-selected-color: red;
    item-size: 50px;
    position: absolute;
    top: 300px;
    left: 0px;
    color: #888888;
    font-size: 42px;
    text-align: center;
}

.panel {
    width: 600px;
    height: 250px;
    margin-left: 75px;
    margin-top: 35px;
    margin-bottom: 35px;
    flex-direction: column;
    justify-content: center;
    border-width: 2px;
    border-style: solid;
    border-color: #DDDDDD;
    background-color: #F5F5F5;
}

.text {
    font-size: 50px;
    text-align: center;
    color: #41B883;
}
</style>

(九)Ui组件库(bui weex)

文档地址:http://dev.bingocc.com/buiweex/docs/env.html

1. 环境搭建

第一步:安装 bui-weex-toolkit

npm install -g bui-weex-toolkit
npm update -g bui-weex-toolkit

第二步:安装IDE
推荐:对于命令行集成的比较好的IDE: WebStorm 和 Visual Studio Code 
WebStorm安装插件:Setting->plugins->输入: VueJS \ Weex \ Sass

2. 创建工程

创建项目: bui-weex create buiWeexDemo&&cd buiWeexDemo &&npm install

  1. 导入到IDE
    npm install node-sass失败:
    npm install --save node-sass --registry=https://registry.npm.taobao.org --disturl=https://npm.taobao.org/dist --sass-binary-site=http://npm.taobao.org/mirrors/node-sass
    
  2. 运行工程
    npm run dev
    npm run serve
    
  3. 集合到一条命令:
    安装依赖:npm install -g concurrently
    package.json: "start":"concurrently \"npm run dev\" \"npm run serve\"",

  4. IDE的可视化命令
    打开package.js右击 ->show npm scripts
    预览页面 http://localhost:8686
    随后,在Weex的Playground中预览该页面

3. bui-weex-toolkit 与 weexPack整合

3.1 调整文件结构

新建 touch weex.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Weex Preview</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="apple-touch-fullscreen" content="yes">
    <meta name="format-detection" content="telephone=no, email=no">
    <link rel="stylesheet" href="./web/assets/style.css">
    <script src="./web/assets/url.js"></script>
    <script src="./web/assets/qrcode.js"></script>
    <script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<h1>Weex Preview</h1>
<div id="app"></div>
<template id="app-template">
    <div id="app">
        <div class="mock-phone">
            <div class="inner">
                <iframe id="preview" src="./index.html"></iframe>
            </div>
            <div class="camera"></div>
            <div class="earpiece"></div>
            <div class="home-btn"></div>
        </div>
        <div id="qrcode">
            <h2>QRCode</h2>
            <a :href="val" target="_blank"><canvas ref="canvas" width="200" height="200"></canvas></a>
            <p class="bundle-url"><a :href="val" target="_blank"></a></p>
        </div>
    </div>
</template>
<script>
    console.log('QR CODE URL: ' + url)
    new Vue({
        el: '#app',
        template: '#app-template',
        data: { val: url },
        mounted: function () {
            var qrcodedraw = new QRCodeLib.QRCodeDraw()
            qrcodedraw.draw(this.$refs.canvas, this.val, function () {})
        }
    })
</script>
</body>
</html>

移动 web/index.html 至根目录
修改 web/assets/url.js

var url = protocol + hostname + port + location.pathname.replace(/\/weex\.html$/, '/').replace(/\/$/, '/dist/index.js')

访问主页: localhost:8082localhost:8082/index.html
访问 二维码页面 localhost:8082/weex.html

3.2 安装依赖

npm install --save node-sass --registry=https://registry.npm.taobao.org --disturl=https://npm.taobao.org/dist --sass-binary-site=http://npm.taobao.org/mirrors/node-sass
npm install --save glob&&npm install --save-dev bui-weex copy-webpack-plugin  sass-loader style-loader 

创建页面(首页,列表,详情)
touch src/index.vue src/list.vue src/detail.vue src/index.js src/list.js src/detail.js
*.vue

<template>
    <div @androidback="back">
        <bui-header title="标题栏" :leftItem="leftItem" :rightItem="rightItem" @leftClick="back" @rightClick="rightclick" @centerClick="centerclick">
        </bui-header>
        <bui-image width="260px" height="260px" src="https://img.alicdn.com/tps/TB1z.55OFXXXXcLXXXXXXXXXXXX-560-560.jpg"></bui-image>
        <bui-image src="/image/demo.jpg" width="300px" height="300px"></bui-image>
        <text class="title">主页</text>
        <list>
            <cell class="cell" @click="gotoPage('list')">
                <text>列表页</text>
            </cell>
            <cell class="cell" @click="gotoPage('detail')">
                <text>详情页</text>
            </cell>
        </list>
    </div>
</template>
<script>
import buiweex from 'bui-weex';
export default {
    data: function() {
        return {
            leftItem: { icon: 'icon-back' },
            rightItem: { text: '更多' }
        }
    },
    methods: {
        back1: function() {
            this.$router.back()
        },
        back: function() {
            buiweex.pop();
            buiweex.toast('left')
        },
        rightclick: function() {
            buiweex.toast('right')
        },
        centerclick: function() {
            buiweex.toast('center')
        }
    }
}
</script>
<style lang="sass" src="bui-weex/src/css/buiweex.scss"></style>
<style>
.wrapper {
    align-items: center;
    margin-top: 120px;
}
.title {
    padding-top: 40px;
    padding-bottom: 40px;
    font-size: 48px;
}
</style>

*.js

import App from './index.vue'
import router from './router'
import mixins from './mixins'
import store from './vuexStore'
import { sync } from 'vuex-router-sync'
import buiweex from 'bui-weex';

const stream = weex.requireModule('stream')
Vue.fetch = stream.fetch;
Vue.prototype.$fetch = stream.fetch;

import * as filters from './filters'
Object.keys(filters).forEach(key => {
    Vue.filter(key, filters[key])
})

sync(store, router)
Vue.mixin(mixins)
var vm = new Vue(Vue.util.extend({
    el: '#root',
    store,
    router,
}, App))

router.push('/')

package.json

"start":"concurrently \"npm run watch\" \"npm run serve\""

Webpack.config.js

{
    test: /\.(scss|sass)$/,
    // include: helpers.root('src', 'app'),
    loader: 'style-loader!css-loader!sass-loader'
}

touch src/mixins.js

var buiweex=require("bui-weex");
    components: {
        'bui-header':buiweex.buiHeader,
        'bui-icon': buiweex.buiIcon,
        'bui-button': buiweex.buiButton,
        'bui-image':buiweex.buiImage,
        'bui-content':buiweex.buiContent,
        'bui-content-scroll':buiweex.buiContentScroll
    }

文件拷贝 webpack.config.js

const copy = require('copy-webpack-plugin');
//  文件拷贝插件,将图片和字体拷贝到dist目录
var copyPlugin = new copy([
    {from: './src/image', to: "./image"},
    {from: './node_modules/bui-weex/src/font', to: "./font"}
])
plugins: [bannerPlugin, copyPlugin]

3.3 引入本地 字体库 ttf 文件

修改 node_modules/bui-weex/src/components/bui-icon.vue

var bundleUrl = weex.config.bundleUrl;
        var url = bundleUrl.split('/').slice(0, -1).join('/');
        url += '/font/iconfont.ttf';
// if (typeof window === 'object') {
        if (weex.config.env.platform == 'Android'||weex.config.env.platform == 'android') {
            url = 'local://dist/font/iconfont.ttf';
        } 
        var domModule = weex.requireModule("dom");
        domModule.addRule('fontFace', {
            'fontFamily': 'iconfont',
            'src': "url('" + url + "')"
        });

(十)安卓加载本地图片

package com.alibaba.weex;

import android.net.Uri;
import android.text.TextUtils;
import android.widget.ImageView;
import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso;
import com.taobao.weex.WXEnvironment;
import com.taobao.weex.WXSDKManager;
import com.taobao.weex.adapter.IWXImgLoaderAdapter;
import com.taobao.weex.common.WXImageStrategy;
import com.taobao.weex.dom.WXImageQuality;

public class ImageAdapter implements IWXImgLoaderAdapter {
    public ImageAdapter() { }
    @Override
    public void setImage(final String url, final ImageView view,
                         WXImageQuality quality, final WXImageStrategy strategy) {
        WXSDKManager.getInstance().postOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (view == null || view.getLayoutParams() == null) {
                    return;
                }
                if (TextUtils.isEmpty(url)) {
                    view.setImageBitmap(null);
                    return;
                } 
                String temp = url;
                temp = temp.replace("../", "");
                temp = temp.replace("./", "");
                temp = temp.replace("file://assets/", "file:///android_asset/");
                System.out.println("原始图片 URL      "+url);

                if (view.getLayoutParams().width <= 0 || view.getLayoutParams().height <= 0) {
                    return;
                }
                if (!TextUtils.isEmpty(strategy.placeHolder)) {
                    Picasso.Builder builder = new Picasso.Builder(WXEnvironment.getApplication());
                    Picasso picasso = builder.build();
                    picasso.load(Uri.parse(strategy.placeHolder)).into(view);
                    view.setTag(strategy.placeHolder.hashCode(), picasso);
                }
                Picasso.with(WXEnvironment.getApplication())
                        .load(temp)
                        .into(view, new Callback() {
                            @Override
                            public void onSuccess() {
                                if (strategy.getImageListener() != null) {
                                    strategy.getImageListener().onImageFinish(url, view, true, null);
                                }
                                if (!TextUtils.isEmpty(strategy.placeHolder)) {
                                    ((Picasso) view.getTag(strategy.placeHolder.hashCode())).cancelRequest(view);
                                }
                            }
                            @Override
                            public void onError() {
                                if (strategy.getImageListener() != null) {
                                    strategy.getImageListener().onImageFinish(url, view, false, null);
                                }
                            }
                        });
            }
        }, 0);
    }
}

Tags: