VUE项目练习笔记

Vue-cms

1. App.vue 组件的基本设置

1.1 header

头部的固定导航栏使用 Mint-UIHeader 组件;

  • 按需导入模块
  • 引用

1.2 底部Tabbar

  1. 底部的页签使用 muitabbar;

  2. 先把 扩展图标的 css 样式和扩展字体库 ttf 文件,拷贝到 项目中

  3. 按需导入

    // 引入Tabbar模块
    import './lib/mui/css/mui.min.css'
    import './lib/mui/css/icons-extra.css'
    import './lib/mui/fonts/mui-icons-extra.ttf'
    
  4. 引用

    • 将底部的页签,改造成 router-link 来实现单页面的切换;
    • 要在中间区域放置一个router-view来展示路由匹配到的组件
  5. 设置路由

  6. Tab Bar 路由激活时高亮 linkActiveClass: 'mui-active'

2. 底部页签的切换动画

  • router-viewtransition包起来
  • 设置动画
    • 注意在 .v-leave-to中设置position: absolute;
    • translate(-100%);translate(100%);

3. Home页

3.1 轮播图

使用 vue-resource获取数据
  • 安装npm install vue-source
  • 引入
import Vueresource from 'vue-resource'
Vue.use(Vueresource)
Home.vue文件
  • 遍历 : v-for="item in LunboList" :key="item.id"
  • img的src一定要加: ==> :src
  • 引入Toast : import { Toast } from 'mint-ui'
  • 将数据保存在data中 this.LunboList = result.body.message

3.2 六宫格

  • 引入MUI中的grid(九宫格)代码
  • 修改样式

4. 新闻资讯

4.1 改造路由链接

  • a -> router-link to /home/newslist
  • 创建vue组件
  • 在router.js中引入组件

4.2 新闻列表页面

- 绘制页面
  • 使用MUI中的media-list
  • h1-> 14px p -> (手机端 12px最合适)
  • dispaly:flex justify-content:space-between
- vue.resource获取数据
  • 设置请求的根目录

    Vue.http.options.root = 'http://www.liulongbin.top:3005';
    
  • 发送请求

    注意:Note that for the root option to work, the path of the request must be relative. This will use this the root option: Vue.http.get('someUrl') while this will not: Vue.http.get('/someUrl')

    this.$http.get('api/getnewslist').then()
    
  • 将数据存放到数组中

  • 在created中调用

- 渲染页面
  • v-for :key
  • :src

4.3 格式化时间

javaScript日期处理类库:Moment.js

  • 安装 npm i moment
  • 导入import moment from 'moment'
  • 在main中定义全局的过滤器
    • Vue.filter()
    • {{item.add_time | dateFormat }}

4.4 新闻详情页面

  • 路由链接 (提供id)
    • to="’/home/newsinfo/’+ item.id"(注意拼接)
    • {path:'/home/newsinfo/:id'}
    • data = > id : this.$router.params.id
  • 绘制页面 -> 获取数据 -> 渲染页面
    • 格式化时间
    • v-html
    • 图片问题 : 去掉scoped

5. 单独封装评论子组件

5.1 创建组件

  • 导入

  • 注册components:{}

  • 写入页面

  • 样式

    • button 幽灵: plain

5.2 获取数据

  • 父向子传值 :id=this.id props:["id"]
  • 默认展示第一页 pageIndex:1
  • i+1

5.3 加载更多

  • 注册点击事件

  • pageIndex++

  • 每当获取新评论数据的时候,不要把老数据清空覆盖,而是应该以老数据,拼接上新数据

     ```js 
    
     ```
    

    this.comments = this.comments.concat(result.body.message)
    ```

5.4 发表评论

  • 文本框双向数据绑定

  • 校验评论内容是否为空

  • 发送请求

    • 全局设置post的表单数据格式

      • this.$http.post(
          'api/postcomment/'+this.id, 
          {content:this.content},
          {emulateJSON:true})
         .then()
        
      • //Vue.http.options.emulateJSON = true;  //全局设置MIME类型
        this.$http.post(
        	'api/postcomment/'+this.id, 
        	{content:this.content})
        .then()
        
    • 拼接评论对象,用unshift添加到数组

6. 图片分享

6.1 顶部滑动条

  • 使用MUI

  • 去掉mui-fullscreen

  • 初始化

    • 先导入 mui 的JS文件:import mui from '../../../lib/mui/js/mui.min.js'
    • 初始化
Bug1:严格模式问题
  • 导入的mui.js会报错

Uncaught TypeError: ‘caller’, ‘callee’, and ‘arguments’ properties may not be accessed on strict mode functions or the arguments objects for calls to them

“调用者”、“被调用者”和“参数”属性可能不会在严格模式函数或调用它们的参数对象上被访问

  • 原因: webpack打包好的bundle.js默认是启用严格模式的,mui.js中用到了"caller",“callee”,"arguments"的东西,两者冲突了
  • 解决: 禁用webpack打包时候的严格模式

移出严格模式: babel-plugin-transform-remove-strict-mode

  • 安装npm install babel-plugin-transform-remove-strict-mode
  • .babelrc文件设置: "plugins": ["transform-remove-strict-mode"]
Bug2:无法滑动问题

tab-top-webview-main组件第一次显示到页面中的时候,无法被滑动

解决: 在 组件的 mounted 事件钩子中,注册 mui 的滚动事件:

 	mounted() {
    	// 需要在组件的 mounted 事件钩子中,注册 mui 的 scroll 滚动事件
        mui('.mui-scroll-wrapper').scroll({
          deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006
        });
  	}
Bug3:滑动时报警告问题
  • 报错

Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

  • 原因:(是chrome为了提高页面的滑动流畅度而新折腾出来的一个东西)

http://www.cnblogs.com/pearl07/p/6589114.html

https://developer.mozilla.org/zh-CN/docs/Web/CSS/touch-action

  • 解决: 可以加上* { touch-action: pan-y; } 这句样式去掉。
Bug4:tabbar无法切换 问题

和 App.vue 中的 router-link 身上的类名 mui-tab-item 存在兼容性问题,导致tab栏失效,可以把mui-tab-item改名为mui-tab-item1,并复制相关的类样式,来解决这个问题;

    .mui-bar-tab .mui-tab-item1.mui-active {
      color: #007aff;
    }

    .mui-bar-tab .mui-tab-item1 {
      display: table-cell;
      overflow: hidden;
      width: 1%;
      height: 50px;
      text-align: center;
      vertical-align: middle;
      white-space: nowrap;
      text-overflow: ellipsis;
      color: #929292;
    }

    .mui-bar-tab .mui-tab-item1 .mui-icon {
      top: 3px;
      width: 24px;
      height: 24px;
      padding-top: 0;
      padding-bottom: 0;
    }

    .mui-bar-tab .mui-tab-item1 .mui-icon~.mui-tab-label {
      font-size: 11px;
      display: block;
      overflow: hidden;
      text-overflow: ellipsis;
    }
获取图片分类列表
  • this.imgcategory.unshift(category)
  • :class="['mui-control-item', item.id == 0 ? 'mui-active':'']"

6.2 图片列表

图片懒加载
  • mint-ui 中的 lazy-load
  • 修改main.js文件
//main.js
import MintUI from 'mint-ui'
Vue.use(MintUI)
// mint-ui样式
import 'mint-ui/lib/style.css'
获取图片
  • 默认加载分类id = 0的图片created(){this.getimagesByCateId(0)}
  • 点击时获取图片@tap="getimagesByCateId(item.id)"
C3样式
  • 3像素问题 : vertical-align:"middle"
  • 边框阴影:box-shadow: 0 0 9px #999;
  • 背景半透明background:rgba(0,0,0,0.4)
  • 统一图片描述高度:max-height:84px;

6.3 图片详情

页面及路由

  • li->router-link tag="li"
  • :to="'/home/photolist/photoinfo/'+item.id"

插入评论组件

<comment :id=id></comment>

vue-preview

vue-preview

  • 装包npm i vue-preview@1.0.5(老版本)

  • img标签上的class不能去掉

  • 每个图片数据中必须有w和h属性

    this.thumimages.forEach(item=>{
        item.w = 600,
        item.h = 400
    }
    

7. 商品购买

7.1 商品列表

垂直均匀分布

  • display: flex;

  • flex-direction: column;

  • justify-content: space-between;

7.2 商品详情

####编程式导航

  • 在网页中有两种跳转方式

    • 标签跳转:使用a标签的形式
    • 编程式导航: 使用window.location.href的形式
    • vue.router 编程式导航this.$router.push({ name: '组件名称', params: { userId }})
  • 实现跳转

    • this.$router.push方法

      godetail(id){
        this.$router.push({name:'goodsinfo',params:{goodsid:id}})
      }
      
    • 路由

      {path:'/home/goodslist/goodsinfo/:goodsid',component:goodsinfo,name:'goodsinfo'}
      

$route$router
  • this.$route是路由参数对象,params,query等都属于它

  • this.$router是有个路由导航对象,可以使用JS代码实现路由的前进后退跳转

使用MUI卡片视图
  • 抽离轮播图为组件

    • 父向子传数据

    • :src问题解决:

      result.body.message.forEach(item=>{
          item.img = item.src
      })
      
    • Vue绑定class :class="{'full':isFull}"

  • 购买数量组件

    • 单独封装一个子组件

    • 使用mui–>numbox

    • 初始化数字选择框钻组件

      • import mui from '../../lib/mui/js/mui.min.js'
        export default {
            mounted(){
              //mounted是实例创建期间的最后一个生命周期函数
              //已经渲染到页面上了 
                mui('.mui-numbox').numbox()
            }
        }
        
  • 渲染数据

7.3 图文介绍

  • 编程式导航跳转goDesc(id){this.$router.push()}

  • 图片问题

    • 去掉scoped
    • width:100%

7.4 商品评论

​ 组件中引用comment组件

7.5 小球动画

  • 定位

  • 半场动画,需要用钩子函数

    • @before-enter=“beforeEnter” (el)
    • @enter=“enter” (el,done)
    • @after-enter=“afterEnter” (el)
    小球优化思路
        enter(el,done){
    // 优化:
    // 1.计算小球初始位置
            const ballPosition = this.$refs.ball.getBoundingClientRect()
        // console.log(ballPosition)
       	//DOMRect {bottom: 279,height: 15,left: 150,right: 165,top: 264,width: 15,x: 150,y: 264}
    //2. 获取徽标在页面中的位置
            const badgePosition = document.getElementById('badge').getBoundingClientRect()
    //3.计算距离
            var movex = badgePosition.left - ballPosition.left
            var movey = badgePosition.top - ballPosition.top
            console.log(movex,movey)
            el.offsetWidth
            el.style.transition = 'all 1s cubic-bezier(.4,-0.3,1,.68)'
            el.style.transform = `translate(${movex}px,${movey}px)`
            done()
        }
    

7.6 加入购物车

设置最大值
  • 父向子传最大值

  • 通过watch属性监听max

    watch:{
        max : function (nVal,oVal) {
           //动态设置数字框最大值
            mui('.mui-numbox').numbox().setOption('max',nVal)
        }
    } 
    

父获取子的value值
    • 向子传递方法@numberVal="getnumber
    • 存储子传过来的数据getnumber(data){ this.numbox = data }
    • 为input框绑定事件@change="changeCount"

    • 向父传值

      changeCount(){
         var num = parseInt(this.$refs.number.value)
         this.$emit('numberVal',num)
      }
      

8. Vuex

8.1 步骤

vuex是为了保存组件之间共享数据而诞生的,是一个全局的共享数据存储区域,就相当于是一个数据的仓库

  • props,data和vuex的区别
  • 安装npm i vuex

  • 创建实例

    import Vuex from 'vuex'
    Vue.use(Vuex)
    const  store = new Vuex.Store({
        state:{//存放数据
        },
        mutations:{
          //mutations的函数列表中最多支持两个参数
    		//- 参数1:state状态
    		//- 参数2:通过commit提交过来的参数(可传一个对象)
        },
        getters:{
          //getters只负责对外提供数据,不负责修改数据
        }
    })
    
    • getters和computed和filters
      • 过滤器和getters都没有修改原数据,都是把原数据做了一层包装,提供给了调用者
      • getters和computed比较像,只要state中的数据发生了变化那么如果getter正好也引用了这个数据,那么就会立即出发getters的重新求职
  • 将vuex实例挂载到vue实例上

8.2 总结

  • state中的数据不能直接修改,若想修改必须通过mutations
  • 若组件想要直接从state上获取数据,需要this.$store.state.***
  • mutations修改数据:this.$store.commit('方法的名称',唯一的一个参数)
  • getters:this.$store.getters.***

9. 加入购物车

9.1 点击事件

  • 点击事件

    addToShopCar (){
    	var goodsinfo={id,count, price,selected}
    	// 调用store-->mutations-->addInfoToCar
    	this.$store.commit('addInfoToCar',goodsinfo)
    }
    
  • 调用mutations里的addInfoToCar方法

    addInfoToCar(state,goodsinfo){
        // 将商品信息添加到car中
        // 如果购物车中已有该商品则只需增加数量,否则添加整个信息
        var flag = false  //假设购物车中没有该商品
        state.car.some(item => {
            if (item.id == goodsinfo.id) {
              item.count += parseInt(goodsinfo.count)
              flag = true
              return true
            }
          })
        if(!flag) {
            state.car.push(goodsinfo)
        }
    }
    

9.2 徽标数值自动更新

  • getters

    getCount(state){
        var c = 0
        state.car.forEach(item=>{
            c += item.count
        })
        return c
    }
    
  • 调用{{this.$store.getters.getCount}}

9.3 实现本地存储

  • addInfoToCar()

    // 当更新car后,将car数组存储在localStorage中
    localStorage.setItem('car',JSON.stringify(state.car))
    
  • 获取car

    // 获取localStorage中的car数组
    var car = JSON.parse(localStorage.getItem('car') || '[]')
    const  store = new Vuex.Store({
        state:{
            car:car
        },
    })
    

10. 购物车商品列表

绘制页面

请求数据

初始化数量值

getGoodsCount(state) {
     var o ={}
     state.car.forEach(item=>{
        o[item.id] = item.count
     })
     return o;
}
<numboxc :num="$store.getters.getGoodsCount[item.id]"></numboxc>

商品数量同步到store中

changeCt(){
    // 将num同步到store中
    this.$store.commit('updateCar',{
        count : this.$refs.numbers.value,
        id : this.goodsid
    })
}
updateCar(state,info){
    state.car.some(item=>{
        if(item.id == info.id){
            item.count = parseInt(info.count)
            return true;
        }
        // return true;  //记住啊,这个bug让你废了一天!
        
    })
    localStorage.setItem('car',JSON.stringify(state.car))
}

删除商品

delgoods(id,i){
  // 列表删除
  this.infolist.splice(i,1)
  // store删除
  this.$store.commit('delgoods',id)
}
//mutations
delgoods(state,id){
    state.car.some((item,i)=>{
        if(item.id == id){
            state.car.splice(i,1)
            return true
        }
    })
    localStorage.setItem('car',JSON.stringify(state.car))
}

选中状态

1. store–>页面
<mt-switch v-model="$store.getters.getSelected[item.id]" @change="switchChange"></mt-switch>
//getters
getSelected(state){
    var s = {}
    state.car.forEach(item=>{
        s[item.id] = item.selected
    })
    return s  //返回的是一个对象
}
2. 页面–> store
<mt-switch
   v-model="$store.getters.getSelected[item.id]" 
   @change="switchChange(item.id,$store.getters.getSelected[item.id])"></mt-switch>
switchChange(id,selected){
  this.$store.commit('switchChange',{
    id:id,
    selected:selected
  })
}
//mutations
switchChange(state,info){
    state.car.forEach(item=>{
        if(item.id == info.id) {
            item.selected = info.selected
        }
    })
    
    localStorage.setItem('car',JSON.stringify(state.car))
}

计算总价和数量

  • 数量

    //getters
    getSelectedCount(state){
        var selectedCount = 0
        state.car.forEach(item=>{
            if(item.selected == true){
                selectedCount += item.count
            }
        })
        return selectedCount;
    }
    
  • 总价

    //getters
    getSumPrice(state){
        var sum = 0
        state.car.forEach(item=>{
            if(item.selected == true){
                sum += item.count * item.price
            }
        })
        return sum;
    }
    

11 返回按钮

  • 点击按钮–>编程式导航this.$router.go(-1)

  • 首页不显示返回

    • v-show="flag"

    • 监听地址 $route.path

      	watch:{
      		"$route.path":function(nVal,oVal){
      			if(nVal == '/home') {
      				this.flag = false
      			} else {
      				this.flag = true
      			}
      		}
      	}
      
    • 刷新判断是否是首页

      created(){
      	this.flag = this.$route.path == '/home'? false : true
      }
      

12. 在手机上预览

  • 保证手机和开发项目的电脑处于同一个WIFI环境中,也就是说手机可以访问到电脑的IP
  • 打开项目中的package.json文件,在dev脚本中添加一个–host指令,把当前电脑的WIFI IP地址设置为–host的指令值
  • 查看自己电脑所处的WIFI的IP
    • 在CMD终端中运行ipconfig,查看无线网的IP地址

–host 192.168.43.202

13. 打包并托管

将项目托管到Apache并启用Gzip压缩

  • 打包
    • 先将根目录里的dist文件删除
    • 运行webpack
  • 托管
    • 将dist里边的index.html和bundle文件托管到apach中
    • phpStudy–>其他选项菜单–>网站根目录
      • 可以直接在127.0.0.1:80中打开

14. 开启Apache的gzip压缩

要让apache支持gzip功能,要用到deflate_Module和headers_Module。打开apache的配置文件httpd.conf,大约在105行左右,找到以下两行内容:(这两行不是连续在一起的)

#LoadModule deflate_module modules/mod_deflate.so
#LoadModule headers_module modules/mod_headers.so

然后将其前面的“#”注释删掉,表示开启gzip压缩功能。开启以后还需要进行相关配置。在httpd.conf文件的最后添加以下内容即可:

<IfModule deflate_module>
    #必须的,就像一个开关一样,告诉apache对传输到浏览器的内容进行压缩
    SetOutputFilter DEFLATE
    DeflateCompressionLevel 9
</IfModule>

最少需要加上以上内容,才可以生gzip功能生效。由于没有做其它的额外配置,所以其它相关的配置均使用Apache的默认设置。这里说一下参数“DeflateCompressionLevel”,它表示压缩级别,值从1到9,值越大表示压缩的越厉害。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值