Weex官方基于Weex和Vue开发了一个的完整项目,在项目中使用了Vuex和vue-router,能够实现同一份代码,在 iOS、Android、Web 下都能完整地工作。weex-hackernews的项目地址。
下载地址:https://github.com/weexteam/weex-hackernews
直接下载zip包下来
1.搭建Weex本地开发环境,可以前往Weex官方按照开发文档教程进行搭建搭建地址:http://weex.apache.org/cn/guide/set-up-env.html。
2.下载开发工具:WebStorm、AndroidStudio、Android SDK、CocoaPods
安装依赖:
npm install
编译代码:
# 生成 Web 平台和 native 平台可用的 bundle 文件
# 位置:
# dist/index.web.js
# dist/index.web.js
npm run build
# 监听模式的 npm run build
npm run dev
拷贝bundle文件:
# 将生成的 bundle 文件拷贝到 Android 项目的资源目录
npm run copy:android
# 将生成的 bundle 文件拷贝到 iOS 项目的资源目录
npm run copy:ios
# run both copy:andriod and copy:ios
npm run copy
# 注意:window系统下,修改下package.json文件,copy:android对应的命令行,官网下载下来的是mac系统命令行,要进行修改
修改前
"copy:android": "cp dist/index.weex.js android/app/src/main/assets/index.js"
修改后:
"copy:android":"xcopy.\\dist\\index.weex.js.\\android\\app\\src\\main\\assets\\index.js"
启动Web服务
npm run serve
启动服务后监听1337端口,访问 http://127.0.0.1:1337/index.html 即可在浏览器中预览页面
启动Android项目
启动Android项目,首先安装Android Studio和Android SDK,并配置好基本的开发环境;用Android Studio 打开 android 目录的项目,等待自动安装完依赖以后,即可启动模拟器或者真机预览页面;
启动 iOS 项目
启动 iOS 项目,首先应该配置好 iOS 开发环境 并且安装 CocoaPods 工具;进入 ios 目录,使用 CocoaPods 安装依赖;
pod install
使用 Xcode 打开ios目录中的项目(HackerNews.xcworkspace),然后即可启动模拟器预览页面。
注:如果想要在真机上查看效果,还需要配置开发者签名等信息。
将项目导入WebStorm里,功能目录分析
|-- android // android工程
|-- dist // android工程
| |--dist/index.web.js //Web平台bundle文件
| |--dist/index.weex.js //native平台bundle文件
|-- ios // ios工程
|-- src //项目的vue文件
| |--components //vue组件(封装组件)
| |--filters //vue的过滤器
| |--mixins //vue的mixins(混合)
| |--store //vuex(vue的状态管理器)
| |--views //视图
| |--App.vue //主UI界面
| |--entry.js //入口文件
| |--router.js //vue的路由声明
|-- .babelrc // ES6语法编译配置
|-- package.json // 配置项目相关信息,通过执行 npm init 命令创建
|-- qrcode.jpg //二维码
|-- README.md // 项目说明
|-- webpack.config.js // 程序打包配置
vue-router是vue.js官方支持的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。
这里vue-router的知识点,这边就不进行阐述,因为官方上有详细的介绍:https://router.vuejs.org/zh-cn/,如果快速入手推荐看之前看过的文章:http://blog.csdn.net/sinat_17775997/article/details/52549123。
router.js文件
import Router from 'vue-router'
import StoriesView from './views/StoriesView.vue'
import ArticleView from './views/ArticleView.vue'
import CommentView from './views/CommentView.vue'
import UserView from './views/UserView.vue'
Vue.use(Router)
// Story view factory
function createStoriesView (type) {
return {
name: `${type}-stories-view`,
render (createElement) {
return createElement(StoriesView, { props: { type }})
}
}
}
export default new Router({
// mode: 'abstract',
routes: [
{ path: '/top', component: createStoriesView('top') },
{ path: '/new', component: createStoriesView('new') },
{ path: '/show', component: createStoriesView('show') },
{ path: '/ask', component: createStoriesView('ask') },
{ path: '/job', component: createStoriesView('job') },
{ path: '/article/:url(.*)?', component: ArticleView },
{ path: '/item/:id(\\d+)', component: CommentView },
{ path: '/user/:id', component: UserView },
{ path: '/', redirect: '/top' }
]
})
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
// mode: 'abstract',
routes: [
{ path: '/top', component: createStoriesView('top') },
{ path: '/new', component: createStoriesView('new') },
{ path: '/show', component: createStoriesView('show') },
{ path: '/ask', component: createStoriesView('ask') },
{ path: '/job', component: createStoriesView('job') },
{ path: '/article/:url(.*)?', component: ArticleView },
{ path: '/item/:id(\\d+)', component: CommentView },
{ path: '/user/:id', component: UserView },
{ path: '/', redirect: '/top' }
]
})
{ path: '/', redirect: '/top' }
{ path: '/top', component: createStoriesView('top') }
如果看完上面给的两个介绍router的链接,就知道path代表匹配路径,component对应组件的文件。可能大家都会问‘/top’对应明明是createStoriesView(‘top’),我们沿着createStoriesView(type)方法看下去:
function createStoriesView (type) {
return {
name: `${type}-stories-view`,
render (createElement) {
return createElement(StoriesView, { props: { type }})
}
}
}
其实会发现在‘/top’对应的文件是StoriesView,其中props对应是传入该组件的参数。
#声明:
"...">
#编程
router.push(...)
router.push({ path: 'home' })
router.push('home')
router.push({ name: 'user', params: { userId: 123 }})
//params跳转到组件传入参数key为userId,value为123
//跳转到的界面接收:this.$router.param.userId;
工程的路由的跳转封装在mixins/index.js文件中:
export default {
methods: {
jump (to) {
if (this.$router) {
this.$router.push(to)
}
}
}
}
大家肯定疑问,mixins/index.js什么时候被注入到vue,能全局调用?其实在入口文件entry.js
// register global mixins.
Vue.mixin(mixins)
Vuex是专为Vue.js应用程序开发的状态管理模型。它采用集中式存储应用的所有组件的状态(理想),并以相应规则保证状态已一种可预测的方式变化。Vuex也是集成到Vuex的官方调试工具
什么是”状态管理模式”?
包含以下几部分:
以下是一个表示“单向数据流”理念的示意
Vuex的基本思想,借鉴Flu、Redux和The Elm Architechture,vuex是专门为Vue.js设计的状态管理库,利用响应体制来进行高效的状态更新。
Store
每个Vuex的应用都有一个核心的store(仓库)。”store”是一个容器,里面存储应用的大部分的状态(state)。相当于一个全局对象,但是有跟全局对象有所区别的是,vuex的状态存储是响应式,只要store状态发生变化,那么相应的引用到的组件会跟着更新。
store的核心内容由State、Getters、Mutations、Actions、Modules组成。
mutation:唯一可以更改state里面的数据;
-Actions:类似mutation,不同在于action提交的是mutation,而不是直接变更数据状态,Actions可以包含任意异步操作。
Modules:使用单一状态树,导致应用的所有状态集中到一个很大的对象。但是,当应用变得很大时,store 对象会变得臃肿不堪,Vuex 允许将 store 分割到模块(module),每个模块拥有自己的 state、mutation、action、getters.
我们直接看下图官方的流程图,state作为全局数据源,我们通过dispatch触发action动作,action做业务处理在提交Mutation来改变State,State改变后自动Render到Vue的component组件上,从而实现单向数据流。
看上面的理论大体懂个流程,有可能存在一知半解,后面直接通过项目的代码进行整个流程进行分析,到时基本可以明白Vuex的流程了。
App.vue 文件
// import Vue from 'vue'
import App from './App.vue' //加载UI主界面
import router from './router' //加载vue路由
import store from './store' //加载vuex的store
import { sync } from 'vuex-router-sync'
import * as filters from './filters' //加载vue的fitlter(过滤器)
import mixins from './mixins' //加载vue的mixins(混合)
sync(store, router)
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
Vue.mixin(mixins)
//vue扩展路由、状态管理器、入口UI主界面
new Vue(Vue.util.extend({ el: '#root', router, store }, App))
router.push('/') //默认路由跳转路径
该文件主要任务是路由(router)、状态管理器(store)、view的导入,
这边的路由的入口路径’/’
router.push('/')
在router中的路由声明我们有提到过的路由的路口路径,就是在这边实现。
StoriesView.vue
其实就是首页界面。
我们可以看到*.vue文件有三部分组成:,