# 全局安装 vue-cli 安装过一次之后,就不用安装了
npm install --global vue-cli
# 创建一个高于 webpack 模板的新项目
vue init webpack my-project
# 安装依赖, 走你
cd my-project
npm install
npm run dev
? Target directory exists. Continue? Yes
? Project name mytravel
? Project description A Vue.js project
? Author ziyuzile
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm
cd my-project
npm run dev #开启服务
8. 把本地代码提交到 码云
git add .
git commit -m 'desc'
git push
home
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
}
]
})
如果报错, 可以修改 config/index.js => useEslint: false,
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import List from '@/pages/list/List'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/list',
name: 'List',
component: List
}
]
})
list
import './assets/styles/reset.css'
import './assets/styles/border.css'
解决 移动端点击有一个300毫秒延迟的bug
# 进入项目目录
npm install fastclick --save
# 安装好之后, 在/package.json 中的 dependencies 就会显示
# 然后再 src/main.js 中, 引入并操作
import fastClick from 'fastclick'
fastClick.attach(document.body)
5. www.iconfont.cn 图标管理
6. 上传到git仓库.
1.1 进入项目目录文件夹, npm install stylus --save
1.2 在安装一个 npm install stylus-loader --save
1.3 使用的时候, 在 style 加入 lang="stylus" 即可.
2.1 在home/components下, 建一个 Header.vue
2.2 在 home/Home.vue 中, 引入,再注册,再使用, 如下
3.1 在 src/assets/styles 新建 iconfont 把 5个文件放入 .eot .svg .ttf .woff .woff2
3.2 把iconfont.css 拷到 styles 中, 并修改里面的路径
3.3 在 src/main.js 中引入 import './assets/styles/iconfont.css'
3.4 使用
3.4 使用 css变量, 在src/assets/styles 新建 varibles.styl 文件夹, 加入 $bgColor=#00bcd4
3.5 在 home/components/Header.vue 中 引用
3.6, 上面的 ../../../ 太长了, 之前学过 @ 可以代表src 文件夹, 这里可以换成 @ ,但是在 style 中, 使用 @ 前面需要加一个 ~, 如下
3.7 上述解决了问题, 但是还不够简洁, 因为styles 这个文件夹经常用过, 可以给他一个别名. PS: 修改了配置,需要重启服务.
# 1. 在 bulid -> webpack.base.conf中, 新加一个别名
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'styles': resolve('src/assets/styles'),
}
},
# 使用
4.1 新建git分支, 把代码放到分支中, 最后合并到 master
git pull # 把线上新建的分支 index-swiper 拉到本地
git checkout index-swiper 切换到分支
#... 编写代码结束
git add . #加入缓冲区
git commit -m 'description' #提交
git push
git checkout master # 切换到主分支
git merge index-swiper 合并到主分支
git push # 提交
4.2 做轮播要用到一个 vue-awesome-swiper 插件, https://github.com/surmon-china/vue-awesome-swiper
4.3 安装 npm install [email protected] --save 加上一个版本号, 稳定.
4.4 在 src/main.js 中引入. 正确引入之后就可以使用 swiper了.
import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'
Vue.use(VueAwesomeSwiper, /* { default global options } */)
4.5 swiper 组件的使用方法, 可以参考githua的地址 PS:这里有一个swiper父级 height=0, 然后用padding-bottom 撑高度, 不错!
5.1 可以安装一个 vue.js devtools 插件, google插件, 可以很好地辅助vue.js 查看数据.
6.1 使用 axios 模块实现ajax , 安装 npm install axios --save
6.2 思想: 在home这个大组件中, 用ajax获取数据, 然后赋给每个小组件, 只请求一次(感觉可以根据情况来定!)
6.3 先引入, 然后再 mounted 钩子中使用.
6.4 为了实现模拟ajax, 可以再static文件夹中放入json, 只有这个目录可以被外部访问到. 在static建 static/mock/index.json
6.5 这里不需要将数据提交到 git, 在根目录 .gitignore 加入 static/mock .
6.6 ajax 的时候, 写 'api/index.json' 可以用转发机制, 转发到 'static/mock/index.json', 在 config/index.js中
module.exports = {
dev: {
// Paths
// ...
proxyTable: {
'/api' :{
target: 'http://localhost:8080',
pathRewrite: {
'^/api' : '/static/mock'
}
}
},
//...
}
}
6.7 代码如下
6.8 父组件ajax获取的数据,传递给子组件, 第一步, ajax中赋值, 然后data中建立1个数据与之对应, 在赋值给子组件,子组件中, 用props注册下, 然后使用
data (){
return {
city: '',
SwiperList : []
}
},
methods: {
getHomeInfo (){
axios.get('/api/index.json')
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res){
res = res.data
if(res.ret && res.data){
const data = res.data
this.city = data.city
this.swiperList = data.swiperList
}
}
},
//子组件
6.9 这里有一个问题, swiper的图, 总是从最后一张开始, 这里需要在swiper加入
6.10 在html中尽量不要使用 运算, 我们修改下, 用 computed计算属性.
总结:
6.11 swiper组件的设置
data (){
return {
swiperOption: {
autoplay: false
}
}
},
1. 配置路由 在 src/router/index.js 中, 新增路由
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/city',
name: 'City',
component: City
}
]
})
2. 在src/pages下新建 city/City.vue, 然后再建一个 city/components 用来放 子模板.
{{city}}
//...
2. 循环一个对象
(item, key) 这个第二个参数是key值. 这里是和数组不同的地方. 如果下面还有循环, 如下.
{{key}}
{{innerItem.name}}
1. 子组件传值给父组件, 父组件再传值给第二个子组件
2. better-scroll 滑动, element 必须是一个dom结点, 所以加一个 [0].
if(this.letter){
const element = this.$refs[this.letter][0]
this.scroll.scrollToElement(element)
}
data (){
return {
touchStatus: false,
startY: 0,
timer: null
}
},
---------------------------------------
if(this.timer){
clearTimeout(this.timer)
}
this.timer = setTimeout( ()=>{
const touchY = e.touches[0].clientY - 79
const index = Math.floor((touchY - this.startY) / 20)
if( index >= 0 && index < this.letters.length){
this.$emit('change', this.letters[index])
}
}, 200)
- {{item}}
1. 地址 https://vuex.vuejs.org/zh/
2. 安装 npm install vuex --save
3. 在 src 目录下, 新建 store/index.js ,按照说明使用
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
city: '北京'
}
})
4. 然后再main.js中, 引入 import store from './store' PS: index.js可以省略
5. 在子组件中使用 {{this.$store.state.city}} 直接使用,不用其他引用, 因为在 main.js 中, 已经引入过了
6. 改变 store 中city的值.
methods: {
handleCityClick (city){
this.$store.dispatch('changeCity', city)
}
},
dispatch 中的 changeCity 对应 index.js 下的 actions . actions下的changeCity, 触发 commit, 对应 mutations下面的 changCity, 以此来改变数据.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
city: '上海'
},
actions: {
changeCity (ctx, city){
ctx.commit('changCity', city)
}
},
mutations: {
changCity (state, city){
state.city = city
}
}
})
7. 以上的例子, 用不到 actions(异步), 可以直接用 mutations, 即是直接只用 commit 即可.
this.$router.push('/')
1. localStorage 是h5出的新功能, 可以实现本地存储 localStorage.city = xxx, 使用的话直接用 localStorage.city.
localStorage 如果客户关闭本地存储或者开启隐身模式, 是有问题的. 所以要用 try catch, (ps: 还不如用 cookie )
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
let defaultCity = '上海'
try {
if(localStorage.city){
defaultCity = localStorage.city
}
}catch (e){}
export default new Vuex.Store({
state: {
city: defaultCity
},
mutations: {
changeCity (state, city){
state.city = cityte
try {
localStorage.city = city
}catch (e){}
}
}
})
2. store/index.js 的代码已经越变越多, 可以拆分一下, 拆分成2个js. 然后倒入
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
Vue.use(Vuex)
export default new Vuex.Store({
state,
mutations
})
export default {
changeCity(state, city) {
state.city = city
try {
localStorage.city = city
} catch (e) {}
}
}
let defaultCity = '上海'
try {
if (localStorage.city) {
defaultCity = localStorage.city
}
} catch (e) {}
export default {
city: defaultCity
}
1. 之前写的 vuex 是 {{this.$store.state.city}} 这样调用的. 还有一种简化写法, 先导入, 然后 映射到 计算属性中. 就可以用 {{this.city}}调用了.
-----------------------------------
{{this.city}}
mapState, 也可以是一个对象
---------------------------------------
{{this.currentCity}}
2. 当改变vuex中数据的时候, 我们之前用到的是 this.$store.commit('changeCity', city) 这样的方法, vuex中也提供了一个方法, 可以直接让我们操作.
import { mapState, mapMutations } from 'vuex'
export default{
name: 'CityList',
methods: {
handleCityClick (city){
// this.$store.commit('changeCity', city)
this.changeCity(city)
this.$router.push('/')
},
...mapMutations(['changeCity'])
},
}
3. 当需要对原始数据进行操作的时候, 用到 vuex 中的 getters ,先在 store/index.js 中定义
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
Vue.use(Vuex)
export default new Vuex.Store({
state,
mutations,
getters: {
doubleCity (state){
return state.city + ' ' + state.city
}
}
})
4. 在文件中导入
--------------------------------------------
{{this.doubleCity}}
1. 每次切换页面的时候, ajax都会重新发送, 但如果有些页面不需要第二次发送的话, 就不需要每次切换的时候, 都重新发送ajax.
2. 在 main.js中, 加入 keep-alive 标签, 页面只会在第一次打开的时候发送 ajax, 后面就算切换回来也不会发送.
3. 但是有些页面还是需要每次都发送ajax请求的. 我们一旦用了 keep-alive来缓存我们的页面, 页面就会多了一个生命周期钩子. activated (){}
export default{
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
data (){
return {
lastCity: '',
swiperList : [],
iconList: [],
recommendList: [],
weekendList: []
}
},
computed: {
...mapState(['city'])
},
methods: {
getHomeInfo (){
axios.get('/api/index.json?city=' + this.city)
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res){
res = res.data
if(res.ret && res.data){
const data = res.data
this.swiperList = data.swiperList
this.iconList = data.iconList
this.recommendList = data.recommendList
this.weekendList = data.weekendList
}
}
},
mounted (){
this.lastCity = this.city
this.getHomeInfo()
},
activated (){
if(this.lastCity !== this.city){
this.lastCity = this.city
this.getHomeInfo()
}
}
}
4. 还有一个方法, 就是不用生命周期钩子, 在 App.vue中, 标明哪个不需要缓存
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/city',
name: 'City',
component: City
},
{
path: '/detail/:id',
name: 'Detail',
component: Detail
}
]
})
1. 在 src下新建 common/gallary/Gallray.vue 把公共组件放在这里
2. 在 build 下的 webpack.base.conf.js 中增加一个 路径引用
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'styles': resolve('src/assets/styles'),
'common': resolve('src/common'),
}
},
3. 在需要的组件中, 引入
4. 完整代码
在使用了 keep-alive 对页面进行缓存的时候, 页面的生命周期会多出来2个钩子, activated, deactivated
我们之前做的 内容页顶部忽隐忽现的时候, 用到了一个 全局 window绑定的函数, 这个时候是有问题的, 因为这样写了, 各个页面都有了这个绑定时间, 这个时候, 要在 deactivated 中给他去掉. 代码如下
activated (){
window.addEventListener('scroll', this.handleScroll)
},
deactivated (){
window.removeEventListener('scroll', this.handleScroll)
}
{{item.title}}
methods: {
getDetailInfo (){
axios.get('/api/detail.json?id=' + this.$route.params.id)
}
},
可以优化一下
methods: {
getDetailInfo (){
axios.get('/api/detail.json', {
params: {
id: this.$route.params.id
}
})
}
},
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/city',
name: 'City',
component: City
},
{
path: '/detail/:id',
name: 'Detail',
component: Detail
}
],
scrollBehavior (to, from, savedPosition){
return { x : 0, y : 0}
}
})
1. src/common 新建 fade/FadeAnimation.vue
2. 在需要的组件中 引用, 先 import , 然后再 components中注册, 再使用
1. 在项目前后端都做好的情况下, 我们之前前端用的是测试数据,现在要修改为后端的数据, 这就是联调. 先删除我们之前用来测试的 static/mock 文件夹
2. 到 config/index.js 中, 这个跨域很重要, 不用提交不出去.
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api' :{
target: 'http://bd.del.com',
changeOrigin: true, //是否跨域
pathRewrite: {
'^/api' : '/api'
}
}
},
}
1. 先查看自己的ip地址, ifconfig, windows ipconfig /all
2. vue.js默认是不允许ip来访问的, 我们来修改一下 根目录下的 package.json , 加上 --host 0.0.0.0
"scripts": {
"dev": "webpack-dev-server --host 0.0.0.0 --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build.js"
},
3. 这样, 手机保持和代码所在电脑, 一个局域网中, 就可以用ip来访问了 http://192.168.2.1:8080/#
4. 这里有一个bug, 城市选择页面, 往下滑的时候, 会带着顶部往下滑, 这里用一个 .prevent 来阻止默认的行为
- {{item}}
安装一个包 npm install babel-polyfill --save
进入 main.js 入口文件 import 'babel-polyfill' 导入即可.
1. 先进入项目目录 运行 npm run build, 完成之后, 会多出来一个 dist 文件夹目录.
2. 把里面的文件放到线上的根目录中即可.
3. 如果想把文件放在一个具体的目录下运行, 则需要修改. config/index.js, 写上我们的地址. 然后重新打包.
我们打包之后, 加载首页的时候, 会加载3个app, 其中最主要的是 app开头的js, 加载首页的时候, 会加载这个js, 这个js中放着所有页面的数据, 是没必要的. 页面小的时候, 是没有什么问题的, 但是随着项目越来越大, 这个 app开头的js会越来越大, 就会出现问题. 如果 app开头的js达到1m以上, 则需要我们用异步组件来优化下.
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: () => import('@/pages/home/Home')
},
{
path: '/city',
name: 'City',
component: () => import('@/pages/city/City')
},
{
path: '/detail/:id',
name: 'Detail',
component: () => import('@/pages/detail/Detail')
}
],
scrollBehavior (to, from, savedPosition){
return { x : 0, y : 0}
}
})
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/city',
name: 'City',
component: City
},
{
path: '/detail/:id',
name: 'Detail',
component: Detail
}
],
scrollBehavior (to, from, savedPosition){
return { x : 0, y : 0}
}
})