vue2.0框架

344 阅读3分钟

1:vue是一套构建用户界面的渐进式框架, 特点:模块化,组件化:在vue中所有的都是组件,mvvm:双向数据绑定 适合做spa, var vm=new Vue({}) 一个vue实例有:挂载元素,数据,模版,方法,生命周期钩子 指令:v-text:绑定普通的文本,{{}}是简写方式 v-if,v-for,v-html:解析html字符串, 属性指令:<img src="{{}}" class与style绑定

1:vuex是一个状态管理,整个应用的状态放到vuex的store中, 提交mutations时改变状态的唯一方法,这个过程是同步的 异步逻辑应该封装到mutation中 vuex的状态存储是响应式的,当vue组件从store读取状态时,store中的状态发生变化,相应的组件也会得到更新,改变store中的状态的唯一途径是显式的提交,整个应用的状态放到vuex的store中, 路由:定义路由组件,const foo={template:'

foo
'}可以通过import 引入 每个路由应该映射一个组件,const routes=[{path:'/foo',component:Foo},{path:'/bar',component:Bar}] 创建router 实例,const router=new VueRouter({routes}) 创建和挂载根实例 const app=new Vue({router}).$mount('#app') 现在应用已启动 移动端选vue不选react: 1:轻量的mvvm一款框架,生态健全,高性能,高可控性,组件化机制 2:便于技术栈同一,vue2.x已经支持pc端,和移动端可以采用相同的技术选型, 3:阿里开源的weex框架基于vue,是一种全新的hybird开发途径, 4:不支持ie8,兼容性要求高的pc少, axios是基于promise 使用:通过npm命令安装npm install axios --save axios发送get请求: axios.get('/url?id="123').then(function(response){ console.log(response) }) .catch(function(err){ console.log(err) }) 或 axios.get('/user',{ params:{ id:123 } }).then(function(response){ console.log(response) }).catch(function(err){ console.log(err) }) 发送post请求 axios.post('/user',{ firstName:'Fred', lastName:'Flintstone' }) .then(function(res){ console.log(res); }) .catch(function(err){ console.log(err); }); 一次性并发多个请求 function getUserAccount(){ return axios.get('/user/12345'); } function getUserPermissions(){ return axios.get('/user/12345/permissions'); } axios.all([getUserAccount(),getUserPermissions()]) .then(axios.spread(function(acct,perms){ //当这两个请求都完成的时候会触发这个函数,两个参数分别代表返回的结果 })) 双向数据绑定的原理:是通过Object.defineproperty() 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。如何传递数据也成了组件的重要知识点之一。 组件与组件之间,还存在着不同的关系。父子关系与兄弟关系(不是父子的都暂称为兄弟吧)。 父子关系即是组件 A 在它的模板中使用了组件 B,那么组件 A 就是父组件,组件 B 就是子组件。 // 注册一个子组件 Vue.component('child', { data: function(){ text: '我是father的子组件!' } template: '{{ text }}' }) // 注册一个父组件 Vue.component('father', { template: '
' // 在模板中使用了child组件 }) 两个组件互不引用,则为兄弟组件 Vue.component('brother1', { template: '
我是大哥
' }) Vue.component('brother2', { template: '
我是小弟
' }) 使用组件的时候:

Prop 子组件想要使用父组件的数据,我们需要通过子组件的 props 选项来获得父组件传过来的数据。以下我使用在 .vue 文件中的格式来写例子。 如何传递数据 在父组件 father.vue 中引用子组件 child.vue,把 name 的值传给 child 组件。

在子组件 child.vue 中的 props 选项中声明它期待获得的数据

那么页面中就会渲染出:Hello linxin

单向数据流 当父组件的 name 发生改变,子组件也会自动地更新视图。但是在子组件中,我们不要去修改 prop。如果你必须要修改到这些数据,你可以使用以下方法: 方法一:把 prop 赋值给一个局部变量,然后需要修改的话就修改这个局部变量,而不影响 prop export default { data(){ newMessage: null
}, props: ['message'], created(){ this.newMessage = this.message; } }

方法二:在计算属性中对 prop 进行处理 export default { props: ['message'], computed{ newMessage(){ return this.message + ' 哈哈哈'; } } }

自定义事件 prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。修改子组件的 prop 值,是不会传回给父组件去更新视图的。那么子组件要如何去与父组件通讯呢? 那就是自定义事件。通过在父组件 on(eventName) 监听自定义事件,当子组件里emit(eventName) 触发该自定义事件的时候,父组件执行相应的操作。 比如在父组件中控制一个弹框子组件的显示,在子组件中按下关闭之后,告诉父组件去隐藏它,然后父组件就执行操作隐藏弹框。

在子组件 dialog.vue 中:

这样就实现了父子组件之间的相互通讯。

Vuex:实现组件与组件间通信, 创建,created 挂载:mounted 更新:update 销毁:destroy vue使用了标准html来写模版,借助极小的模版语法可以做一些简单的事,创建重复的基于视图数据的元素。 vue2.0几个常用知识点:路由懒加载, { path:'/development', name:'development', component:(resolve)=>{ require(['../views/development.vue'], resolve) } } 通过路由的的beforeEach钩子函数来判断是否需要登录 { path: '/system', name: '系统设置', meta: { login: true }, component: import('System/index') } router.beforeEach((to, from, next) => { if (to.meta.login) { //判断前往的界面是否需要登陆 if (store.state.user.user.name) { // 是否已经登陆 next() }else{ Vue.prototype.$alert('请先登录!') .then( () => { store.state.user.isLogin = true }) } }else{ if (to.meta.page) store.state.app.pageLoading = true next() }

}) 动画切换:通过检测设置在 Router上的animate属性 来判断它做什么样的切换动画。 Router.prototype.animate = 0 // 获取每个路由meta上面的slide 来判断它做什么动画 { path: '/system', name: '系统设置', meta: { slide: 1 }, component: import('System/index') } watch: { $route (to, from) {

0: 不做动画
1: 左切换
2: 右切换
3: 上切换
4: 下切换
let animate = this.$router.animate || to.meta.slide
if (!animate) {
this.animate = '' 
}else{
this.animate = animate === 1 ?  'slide-left' :
animate === 2 ?  'slide-right' :
animate === 3 ?  'slide-top' :
animate === 4 ?  'slide-bottom' : ''
}
this.$router.animate = 0
}

} 项目目录:├─.babelrc // babel 配置文件 ├─index.template.html // html 模板文件 ├─server.js // 提供服务端渲染及 api 服务 ├─src // 前端代码 | ├─app.js // 主要用于创建 vue 实例 | ├─App.vue // 根组件 | ├─entry-client.js // 客户端渲染入口文件 | ├─entry-server.js // 服务端渲染入口文件 | ├─stores // vuex 相关 | ├─routes // vue-router 相关 | ├─components // 组件 ├─dist // 代码编译目标路径 ├─build // webpack 配置文件 搭建vue开发环境:利用 webpack 可以非常快速的搭建一个简单的 vue 开发环境,可以直接乘电梯前往。 为了高效地进行开发,vue 开发环境应该有代码热加载和请求转发的功能。这些都可以使用 webpack-dev-server 来轻松实现,只需配置 webpack 的 devServer 项: module.exports = merge(baseWebpackConfig, { devServer: { historyApiFallback: true, noInfo: true, overlay: true, proxy: config.proxy }, devtool: '#eval-source-map', plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.template.html', inject: true // 插入css和js }), new webpack.HotModuleReplacementPlugin(), new FriendlyErrors() ] }) 启动时添加--hot参数即可。 在项目根目录下运行 npm run server 启动后端 api 服务,然后运行 npm run dev ,webpack 会自动在默认浏览器中打开 http://localhost:8080 地址,看到效果。

要实现服务端渲染,只需增加如下 webpack 配置: module.exports = merge(baseWebpackConfig, { entry: './src/entry-server.js', // 告知 vue-loader 输送面向服务器代码(server-oriented code)。 target: 'node', output: { filename: 'server-bundle.js', libraryTarget: 'commonjs2', }, plugins: [ new VueSSRServerPlugin() ] })

entry 的文件路径跟之前的不太一样,这里使用的是专门为服务端渲染准备的入口文件。 import { createApp } from './app' // 这里的 context 是服务端渲染模板时传入的 export default context => { // 因为有可能会是异步路由钩子函数或组件,所以我们将返回一个 Promise, // 以便服务器能够等待所有的内容在渲染前, // 就已经准备就绪。 return new Promise((resolve, reject) => { const { app, router, store } = createApp()

const { url } = context
const { fullPath } = router.resolve(url).route

if (fullPath !== url) {
  return reject({ url: fullPath })
}

router.push(url)

// 等到 router 将可能的异步组件和钩子函数解析完
router.onReady(() => {
  const matchedComponents = router.getMatchedComponents()
  // 匹配不到的路由,执行 reject 函数,并返回 404
  if (!matchedComponents.length) {
    return reject({ code: 404 })
  }

  // 执行所有组件中的异步数据请求
  Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({
    store,
    route: router.currentRoute
  }))).then(() => {
    context.state = store.state
    resolve(app)
  }).catch(reject)
}, reject)

}) }

其中的 asyncData 可能会让人疑惑,稍后我们用一个例子来说明。现在,然我们来编译一下,运行 npm run build:server ,将会在 dist 目录下得到 vue-ssr-server-bundle.json 文件。可以看到,该文件包含了 webpack 打包生成的所有 chunk 并指定了入口。后面服务端会基于该文件来做渲染。

// 服务端渲染 server.get('*', (req, res) => { const context = { url: req.originalUrl } renderer.renderToString(context, (err, html) => { if (err) { if (err.code === 404) { res.status(404).end('Page not found') } else { res.status(500).end('Internal Server Error') } } else { res.end(html) } }) }) 新增代码不多,首先使用上面生成的文件创建了一个 renderer 对象,然后调用其 renderToString 方法并传入包含请求路径的对象作为参数来进行渲染,最后将渲染好的数据即 html 返回。 运行 npm run server 启动服务端,打开 http://localhost:8081 就可以看到效果了:

关于 asyncData: 前面提到了 asyncData ,现在以该例子来梳理一下。首先,看看组件中的代码:

这是一个很简单的组件,包括一个列表,该列表的内容通过请求从后端获取,一个表单,用于提交新的记录到后端保存。其中 asyncData 是我们约定的函数名,表示渲染组件需要预先执行它获取初始数据,它返回一个 Promise,以便我们在后端渲染的时候可以知道什么时候该操作完成。这里,该函数触发了 fetchItems 以更新 store 中的状态。还记得我们的 entry-server.js 文件吗,里面正是调用了组件的 asyncData 方法来进行数据预取的。 在开发阶段,我们同样需要进行数据预取,为了复用 asyncData 代码,我们在组件的 beforeMount 中调用该方法,我们将这个处理逻辑通过 Vue.mixin 混入到所有的组件中: Vue.mixin({ beforeMount() { const { asyncData } = this.options
    if (asyncData) {
      // 将获取数据操作分配给 promise
      // 以便在组件中,我们可以在数据准备就绪后
      // 通过运行 `this.dataPromise.then(...)` 来执行其他任务
      this.dataPromise = asyncData({
        store: this.store, route: this.$route }) } } }) 我们生成的 html 中并没有引入任何 js,用户无法进行任何交互,比如上面的列表页,用户无法提交新的内容。当然,如果这个页面是只给爬虫来“看”的话这样就足够了,但如果考虑到真实的用户,我们还需要在 html 中引入前端渲染的 js 文件。 前端渲染部分需要先增加一个 webpack 的配置文件用于生成所需的 js, css 等静态文件: module.exports = merge(baseWebpackConfig, { plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false, drop_console: true } }), // 重要信息:这将 webpack 运行时分离到一个引导 chunk 中, // 以便可以在之后正确注入异步 chunk。 // 这也为你的 应用程序/vendor 代码提供了更好的缓存。 new webpack.optimize.CommonsChunkPlugin({ name: "manifest", minChunks: Infinity }), // 此插件在输出目录中 // 生成 vue-ssr-client-manifest.json。 new VueSSRClientPlugin() ] }) 同时,前端渲染还需要有自己的入口文件 entry-client,该文件在讲 asyncData 的时候有所提及: import Vue from 'vue' import { createApp } from './app.js' // 客户端特定引导逻辑…… const { app, router, store } = createApp() if (window.INITIAL_STATE) { store.replaceState(window.INITIAL_STATE) }

Vue.mixin({ beforeMount() { const { asyncData } = this.options
    if (asyncData) {
      // 将获取数据操作分配给 promise
      // 以便在组件中,我们可以在数据准备就绪后
      // 通过运行 `this.dataPromise.then(...)` 来执行其他任务
      this.dataPromise = asyncData({
        store: this.store, route: this.route
      })
    }
  }
})
// 这里假定 App.vue 模板中根元素具有 `id="app"`
router.onReady(() => {
  app.mount('#app') }) 在server.js中: const clientManifest = require('./dist/vue-ssr-client-manifest.json')

const renderer = createBundleRenderer(bundle, { template: fs.readFileSync('./index.template.html', 'utf-8'), clientManifest }) 然后 npm run server 启动服务,再打开 http://localhost:8081,可以看到渲染后的 html 文件中已经引入了 js 资源了。

从搭建一个简单的 vue 开发环境开始,然后基于此实现了服务端渲染,并引入了客户端渲染所需的资源。通过这个过程跑通了 vue 服务端渲染的大致流程。 服务端渲染其实主要是用来解决seo的问题,所以可以在服务端通过请求头判断来源并做不同处理,若是爬虫则进行服务端渲染(不需要引入客户端渲染所需的资源),若是普通用户则还是用原始的客户端渲染方式。