├── static
└── .gitkeep
├── config
├── prod.env.js
├── dev.env.js
└── index.js
├── src
├── assets
│ └── logo.png
├── components
│ ├── next1.vue
│ └── HelloWorld.vue
├── main.js
├── router
│ └── index.js
└── App.vue
├── .editorconfig
├── .gitignore
├── .babelrc
├── .postcssrc.js
├── index.html
├── package.json
└── README.md
/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mosasa/vue-transition-practice/HEAD/src/assets/logo.png
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Editor directories and files
9 | .idea
10 | .vscode
11 | *.suo
12 | *.ntvs*
13 | *.njsproj
14 | *.sln
15 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-vue-jsx", "transform-runtime"]
12 | }
13 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | "postcss-import": {},
6 | "postcss-url": {},
7 | // to edit target browsers: use "browserslist" field in package.json
8 | "autoprefixer": {}
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | test
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/next1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 上一页
4 |
5 |
6 |
11 |
19 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import Vue from 'vue'
4 | import App from './App'
5 | import router from './router'
6 |
7 | Vue.config.productionTip = false
8 |
9 | /* eslint-disable no-new */
10 | new Vue({
11 | el: '#app',
12 | router,
13 | components: { App },
14 | template: ''
15 | })
16 |
--------------------------------------------------------------------------------
/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 下一页
4 |
5 |
6 |
7 |
17 |
18 |
19 |
27 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import HelloWorld from '@/components/HelloWorld'
4 | import Next1 from '@/components/next1'
5 |
6 | Vue.use(Router)
7 |
8 | export default new Router({
9 | routes: [
10 | {
11 | path: '/',
12 | name: 'HelloWorld',
13 | component: HelloWorld
14 | },
15 | {
16 | path: '/next1',
17 | name: 'Next1',
18 | component: Next1
19 | }
20 | ],
21 | // scrollBehavior (to, from, savedPosition) {
22 | // return { x: 0, y: 0 }
23 | // }
24 | })
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "wms <1462546799@qq.com>",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "build": "node build/build.js"
11 | },
12 | "dependencies": {
13 | "vue": "^2.5.2",
14 | "vue-router": "^3.0.1"
15 | },
16 | "devDependencies": {
17 | "autoprefixer": "^7.1.2",
18 | "babel-core": "^6.22.1",
19 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
20 | "babel-loader": "^7.1.1",
21 | "babel-plugin-syntax-jsx": "^6.18.0",
22 | "babel-plugin-transform-runtime": "^6.22.0",
23 | "babel-plugin-transform-vue-jsx": "^3.5.0",
24 | "babel-preset-env": "^1.3.2",
25 | "babel-preset-stage-2": "^6.22.0",
26 | "chalk": "^2.0.1",
27 | "copy-webpack-plugin": "^4.0.1",
28 | "css-loader": "^0.28.0",
29 | "extract-text-webpack-plugin": "^3.0.0",
30 | "file-loader": "^1.1.4",
31 | "friendly-errors-webpack-plugin": "^1.6.1",
32 | "html-webpack-plugin": "^2.30.1",
33 | "node-notifier": "^5.1.2",
34 | "optimize-css-assets-webpack-plugin": "^3.2.0",
35 | "ora": "^1.2.0",
36 | "portfinder": "^1.0.13",
37 | "postcss-import": "^11.0.0",
38 | "postcss-loader": "^2.0.8",
39 | "postcss-url": "^7.2.1",
40 | "rimraf": "^2.6.0",
41 | "semver": "^5.3.0",
42 | "shelljs": "^0.7.6",
43 | "uglifyjs-webpack-plugin": "^1.1.1",
44 | "url-loader": "^0.5.8",
45 | "vue-loader": "^13.3.0",
46 | "vue-style-loader": "^3.0.1",
47 | "vue-template-compiler": "^2.5.2",
48 | "webpack": "^3.6.0",
49 | "webpack-bundle-analyzer": "^2.9.0",
50 | "webpack-dev-server": "^2.9.1",
51 | "webpack-merge": "^4.1.0"
52 | },
53 | "engines": {
54 | "node": ">= 6.0.0",
55 | "npm": ">= 3.0.0"
56 | },
57 | "browserslist": [
58 | "> 1%",
59 | "last 2 versions",
60 | "not ie <= 8"
61 | ]
62 | }
63 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.3.1
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | proxyTable: {},
14 |
15 | // Various Dev Server settings
16 | host: 'localhost', // can be overwritten by process.env.HOST
17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
18 | autoOpenBrowser: false,
19 | errorOverlay: true,
20 | notifyOnErrors: true,
21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
22 |
23 |
24 | /**
25 | * Source Maps
26 | */
27 |
28 | // https://webpack.js.org/configuration/devtool/#development
29 | devtool: 'cheap-module-eval-source-map',
30 |
31 | // If you have problems debugging vue-files in devtools,
32 | // set this to false - it *may* help
33 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
34 | cacheBusting: true,
35 |
36 | cssSourceMap: true
37 | },
38 |
39 | build: {
40 | // Template for index.html
41 | index: path.resolve(__dirname, '../dist/index.html'),
42 |
43 | // Paths
44 | assetsRoot: path.resolve(__dirname, '../dist'),
45 | assetsSubDirectory: 'static',
46 | assetsPublicPath: '/',
47 |
48 | /**
49 | * Source Maps
50 | */
51 |
52 | productionSourceMap: true,
53 | // https://webpack.js.org/configuration/devtool/#production
54 | devtool: '#source-map',
55 |
56 | // Gzip off by default as many popular static hosts such as
57 | // Surge or Netlify already gzip all static assets for you.
58 | // Before setting to `true`, make sure to:
59 | // npm install --save-dev compression-webpack-plugin
60 | productionGzip: false,
61 | productionGzipExtensions: ['js', 'css'],
62 |
63 | // Run the build command with an extra argument to
64 | // View the bundle analyzer report after build finishes:
65 | // `npm run build --report`
66 | // Set to `true` or `false` to always turn it on or off
67 | bundleAnalyzerReport: process.env.npm_config_report
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
33 |
115 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 写在前面的思考
2 | * 如何匹配到相应需要跳转的页面?
3 | * 如何判断是“前进”还是“后退”而后使用不同的动画方式?
4 | * 如何对不同的跳转设置动画效果?
5 | ## 实现过程
6 | ### 一、vue路由匹配
7 | 创建vue实例,匹配路由。
8 | 用Vue.js + Vue Router创建单页应用,是非常简单的。使用Vue.js,我们可以通过组合组件来组成应用程序,将Vue Router 添加进来之后,我们需要做的是,将组件(components)映射到路由(routes),然后告诉Vue Router 在哪里渲染它们。
9 |
10 | ```
11 | import Vue from 'vue'
12 | import Router from 'vue-router'
13 | Vue.use(Router)
14 | //如果使用模块化机制编程,导入Vue和VueRouter,就需要调用Vue.use(Router)
15 | ```
16 | 接下来就可以进行路由组件的映射:
17 | (路由)组件的定义可以自行定义,当然,为了践行模块化组件化思想,多是从其他文件import进来。以下以简单的“登录->主页->点单->结算”四个页面的交互为例:
18 |
19 | ```
20 | import Login from '@/components/login'
21 | import Index from '@/components/index'
22 | import PointList from '@/components/pointList/pointList'
23 | import SettLement from '@/components/pointList/settlement'
24 |
25 | //创建router实例,然后传入‘routes’配置
26 | export default new Router({
27 | //routes配置可以直接传入,也可以先定义后使用
28 | //每个路由都应该映射一个组件,其中component可以是通过Vue.extend()创建的组件构造器,或者只是一个组件配饰对象。(今天暂时不考虑嵌套路由的情况)
29 | routes: [
30 | {
31 | path: '/', // 登录
32 | name: 'Login',
33 | component: Login
34 | },
35 | {
36 | path: '/index', // 主页
37 | name: 'Index',
38 | component: Index
39 | },
40 | {
41 | path: '/pointList', // 点单
42 | name: 'PointList',
43 | component: PointList
44 | },
45 | {
46 | path: '/settLement', // 结算
47 | name: 'SettLement',
48 | component: SettLement
49 | }
50 | ]
51 | })
52 | ```
53 | ### 二、路由跳转 $router
54 |
55 | ```
56 | 组件路由除了使用全局组件 router-link 来实现点击跳转(相当于按钮)外,还可以使用组件本身具有的一个实例对象$router及其一些属性来达到目标。
57 | $router 是VueRouter的一个实例对象,相当于一个全局的路由器对象。在Vue实例内部,你可以通过$router访问路由实例,里面含有很多属性和子对象
58 | ,例如history对象,经常用到的跳转链接就可以调用this.$router.push,this.$router.push会往history栈中添加一个新记录。
59 | ```
60 | 声明式|编程式
61 | ---|:--:
62 | 等同于调用 router.push(...)
64 | (...)该方法的参数可以是一个字符串,或者一个描述地址的对象:
65 |
66 | ```
67 | // 字符串
68 | router.push('home')
69 | // 对象
70 | router.push({ path: 'home' })
71 | // 命名的路由
72 | router.push({ name: 'user', params: { userId: 123 }})
73 | // 带查询参数,变成 /register?plan=private
74 | router.push({ path: 'register', query: { plan: 'private' }})
75 | ```
76 | 组件路由跳转实例:
77 | 1.
78 | ```
79 |
80 |
81 | 点单
82 |
83 | ```
84 | 2.
85 |
86 | ```
87 |
88 | //Js:
89 | methods: {
90 | goPage(url, param) {
91 | this.$router.push({ name: url });
92 | }
93 | }
94 | ```
95 | ### 三、vue路由对象$route(只读)
96 | 在使用了vue-router的应用中,路由对象会被注入每个组件中,赋值为this.$route,并且当路由切换时,路由对象会被更新。所以route相当于当前正在跳转的路由对象,可以从里面获取name,path,params,query等,即包含了当前URL解析得到的信息,还有URL匹配到的路由记录。
97 | 路由对象暴露了以下属性(常见):
98 | 1. $route.path
99 |
100 | ```
101 | 字符串(string)。等于当前路由对象的路径,会被解析为绝对路径,如:http://example.com/#/login?name=aa,this.$route.path
102 | ,输出“/login”,即对应上面1中路由匹配时routes配置中的“path”;
103 | ```
104 | 2. $route.name
105 | 字符串(string)。有时候,通过一个名称来标识一个路由显得更加方便,特别是在链接一个路由,或者是执行一些跳转的时候。同样,这里的name也对应了routes配置中给某个路由设置名称的name值:
106 | 要链接到一个命名路由,可以给router-link的to属性传一个对象:
107 |
108 | ```
109 |
110 |
111 | ```
112 | 用在调用router.push()中也是一回事:
113 |
114 | ```
115 | this.$router.push({ name: 'Order', params: { userId: 123 }})
116 | ```
117 | 3. $route.params
118 | 对象(object)。路由跳转携带参数:
119 |
120 | ```
121 | this.$router.push({ name: 'Order', params: { userId: 123 }})
122 | console.log(this.$route.params.userId); //123
123 | ```
124 | 4. $route.query
125 | 对象(object)。可访问携带的查询参数:
126 |
127 | ```
128 | this.$router.push({name: 'login', query:{name: 'userName'}});
129 | this.$route.query.name; //you
130 | //此时路由为:"http://example.com/#/login?name=userName。"
131 | ```
132 | 5. $route.redirectedFrom
133 | 字符串(string)。重定向来源:
134 | ```
135 | 如:{ path: '*',redirect: {name: 'hello'}}
136 | 此时访问不存在的路由http://example.com/#/a会重定向到hello,
137 | 在hello访问this.$route.redirectedFrom; 输出“/a”。
138 | ```
139 | 6. $route.matched
140 | 数组(array)。当前路由下路由声明的所有信息,从父路由(如果有)到当前路由为止。
141 | 7. $route.hash
142 | 字符串(string)。当前路径的hash值。
143 | ### 四、vue监听$route的方式
144 |
145 | ```
146 | watch:{‘$route’ (to, from) {}}
147 | ```
148 | $route 作为vue实例的一个响应式属性,和在data中写的属性本质上是一样的,都可以通过this的方式拿到。既然你可以监听data中的属性变化,同样也可以监听 $route 的变化。watch中监听的对象默认回调函数中的参数值就是newVal,oldVal。作为 $route 属性来说当然也就是 to 和 from 的概念了。
149 | Vue用router.push(传参)跳转页面,参数改变,在跳转后的路由观察路由变化,进行页面刷新,可对“from->to”的过程设置动画效果。
150 | 该功能的难点就在于怎样获取“上一页”和“下一页”,即怎样分辨是“前进”还是“后退”?
151 | 例:
152 |
153 | ```
154 | // watch $route 决定使用哪种过渡
155 | watch:{
156 | '$route' (to, from) {
157 | //此时假设从index页面跳转到pointList页面
158 | console.log(to); // "/pointList"
159 | console.log(from); // “/index”
160 | const routeDeep = ['/', '/index','/pointList', '/settLement'];
161 | const toDepth = routeDeep.indexOf(to.path)
162 | const fromDepth = routeDeep.indexOf(from.path)
163 | this.transitionName = toDepth > fromDepth ? 'fold-left' : 'fold-right'
164 | }
165 | },
166 | ```
167 | to、from是最基本的路由对象,分别表示从(from)某个页面跳转到(to)另一个页面,to.path(表示要跳转到的路由地址),from.path同理。
168 | 定义routeDeep数组,将路由目录按层级依次排序(暂不考虑嵌套路由的情况),复杂单页应用里,同一层级(如同一页面上的多个导航按钮)顺序随意,然后依次排列每个导航的下一页、下下页…即保证每个“上一页”在“下一页”前面。
169 | 总结下来就是:按照routeDeep数组里定义的路由目录的顺序,“toDepth > fromDepth”表示“上一页”跳转到“下一页”,同理可由此判断是“前进”还是“后退”。
170 | ### 五、Vue2.0中transition组件的使用
171 |
172 | ```
173 |
174 |
175 |
176 | ```
177 | * transition中只有name属性,不可添加其他标签属性。
178 | * transition中只能有一个子元素并且该子元素需要有v-show或者v-if来控制是否显示。
179 | ##### 过渡CSS类名
180 | transition中的name属性用于 替换 vue钩子函数中的类名transitionName-
181 | * transitionName-enter: 定义进入过渡的开始状态。在元素被插入时生效,在下一个帧移除。
182 | * transitionName-enter-active: 定义进入过渡的结束状态。在元素被插入时生效,在transition/animation完成之后移除。
183 | * transitionName-leave:定义离开过渡的开始状态。在离开过渡被触发时生效,在下一个帧移除。
184 | * transitionName-leave-active: 定义离开过渡的结束状态。在离开过渡被触发时生效,在transition/animation完成之后移除。
185 | ```
186 | this.transitionName = toDepth > fromDepth ? 'fold-left' : 'fold-right'
187 | ```
188 | 在“watch $route”中,判断页面跳转的“前进”和“后退”时,决定用不同的过渡效果(fold-left还是fold-right)。
189 | ### 六、animation、transform动画效果实现
190 | 在上一个主题中,判断页面跳转路径之后,为两种跳转的transition设置不同的类名“fold-left”、“fold-right”。
191 | 然后在CSS中,为两种类名设置不同的动画效果(这里以“左滑动”和“右滑动”为例):
192 |
193 | ```
194 | .fold-left-enter-active {
195 | animation-name: fold-left-in;
196 | animation-duration: .3s;
197 | }
198 | .fold-left-leave-active {
199 | animation-name: fold-left-out;
200 | animation-duration: .3s;
201 | }
202 | .fold-right-enter-active {
203 | animation-name: fold-right-in;
204 | animation-duration: .3s;
205 | }
206 | .fold-right-leave-active {
207 | animation-name: fold-right-out;
208 | animation-duration: .3s;
209 | }
210 | ```
211 | animation 属性是一个简写属性,用于设置六个动画属性:
212 | 值|描述
213 | ---|:--:
214 | animation-name|规定需要绑定到选择器的 keyframe 名称。
215 | animation-duration|规定完成动画所花费的时间,以秒或毫秒计。
216 | animation-timing-function|规定动画的速度曲线。
217 | animation-delay|规定在动画开始之前的延迟。
218 | animation-iteration-count|规定动画应该播放的次数。
219 | animation-direction|规定是否应该轮流反向播放动画。
220 |
221 | ```
222 | @keyframes fold-left-in {
223 | 0% {
224 | transform: translate3d(100%, 0, 0);
225 | }
226 | 100% {
227 | transform: translate3d(0, 0, 0);
228 | }
229 | }
230 | @keyframes fold-left-out {
231 | 0% {
232 | transform: translate3d(0, 0, 0);
233 | }
234 | 100% {
235 | transform: translate3d(-100%, 0, 0);
236 | }
237 | }
238 | ```
239 | 根据CSS3 @keyframes规则,创建动画。创建动画的原理即将一套CSS样式逐渐变化为另一套样式。在动画过程中,能够多次改变这套CSS样式。可以“百分比”来规定改变发生的时间,或者通过关键词“from”和“to”,等价于“0%”(动画的开始时间)和“100%”(动画的结束时间)。一般为了获得最佳的浏览器支持,应该始终定义0%和100%选择器。
240 | transform属性向元素应用2D或3D转换。该属性允许我们对元素进行旋转、缩放、移动或倾斜。translate3d(x,y,z)定义3D转换,如transform:translate3d(100%, 0, 0)只改变了x的值,即代表横向左滑动,同理可相应推出其他情况。
241 | ## 总结
242 | 以上就是vue页面跳转动画效果功能实现的6个步骤,即这个功能中所含括的6个大知识点,当然其中还包括许多扩展的知识点,学无止境,需慢慢深入挖掘…
243 |
--------------------------------------------------------------------------------