SlideShare a Scribd company logo
以Vue開發電⼦子商務網站

架構與眉⾓角
2017/8/22
• Mayu Chen

• Frontend Developer of gelovery.com / patio.tw

• windfish27@gmail.com

๏ 以下範例例為經驗分享

๏ 感謝後端⼤大⼤大的⽀支援...m(_ _)m
About me
• 專案環境簡介

• 初始專案建置

• Css Style 

• 網站路路由

• 狀狀態管理理

• 溝通Server

• 取得⾴頁⾯面資料
Agenda
專案環境簡介
• Docker

• Rails

• Vue (SPA)

• Node

• Yarn

• Atom

• Package: languague-vue

• Chrome extension

• Vue.js devtools
專案技術列列表
• Html / Css

• Scss (Sass)

• Javascript (ES6)、JSON

• Vue

• Vue-router / Vuex / Vue-resource

• Webpack
$ npm install -g vue-cli
$ vue init webpack vue-new
⾃自訂專案名稱
Template variables:

https://github.com/vuejs/vue-cli
初始專案建置:Vue-cli
Preference: Github: Vuejs/vue-cli
This will install Vue 2.x version of the template.
For Vue 1.x use: vue init webpack#1.0 vue-new
? Project name vue-new
? Project description A Vue.js project
? Author Meiyu <windfish27+GH@gmail.com>
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Setup unit tests with Karma + Mocha? No
? Setup e2e tests with Nightwatch? No
vue-cli Generated "vue-new".
To get started:
cd vue-new
npm install
npm run dev
Documentation can be found at https://vuejs-templates.github.io/
webpack
$ npm run build ⟶ Production ready build.
$ npm run unit ⟶ Unit tests run.
$ npm run e2e ⟶ End-to-end tests.
$ cd vue-new
$ npm install / yarn
$ npm run dev
初始專案建置:Vue-cli
初始專案建置:Vue-cli專案結構
─ build/
─ config/
─ node_modules/
─ src/
 ├ assets/
 ├ components/
  └ Hello.vue
 ├ router/
  └ index.js
 ├ App.vue
 └ main.js
─ static/
─ index.html
─ package.json
─ yarn.lock
初始專案建置:vue-template
<template lang="html">
html or other html loader
</template>
<script>
export default {
vue methods
}
</script>
<style lang="css">
css or other css loader
</style>
*.vue
import Vue from 'vue'
import App from './App'
import router from './
router'
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
main.js
初始專案建置:Vue-cli初始檔案
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Project Name</
title>
</head>
<body>
<div id="app"></div>
<!-- built files will be
auto injected -->
</body>
</html>
index.html
初始專案建置:Vue-cli初始檔案
<template>
<div id="#app">
<img src="./assets/logo.png">
<router-view></router-view>
<hello></hello>
</div>
</template>
<script>
import Hello from './components/Hello'
export default {
name: 'app',
components: { Hello }
}
</script>
App.vue
<style lang="css">
…
</style>
初始專案建置:⾃自訂專案結構
─ src/
 ├ api/
 ├ assets/
 ├ components/
 ├ mixins/
 ├ router/
 ├ scss/
 ├ store/
 ├ pages/
 ├ vue-plugin/
 ├ App.vue
 └ main.js
$ npm install node-sass sass-loader
$ yarn add node-sass sass-loader
<style lang="scss">
.style{
…
}
</style>
<style lang="sass">
.style
…
</style>
Css Style:Scss
<style lang="scss">
@import '../scss/function/
function';
.component-style{
…
}
</style>
Css Style:Scss
$variables: $color;
$width: 640px;
…
scss/function/_variables.scss
@mixin function-name($value) {
css-proprety: $value;
}
…
scss/function/_mixins.scss
@import 'mixins';
@import 'variables';
scss/function/function.scss
*.vue
Css Style:Scss
<style lang="scss">
@import 'scss/style';
</style>
App.vue
@import 'base/base';
@import 'function/function';
@import 'plugins/plugins';
@import 'partials/partials';
@import 'components/components';
@import 'views/views';
scss/style.scss
眉⾓角:iOS safari scroll
-webkit-overflow-scrolling: touch; // iOS safari scroll smooth
• Fixed footer範例例

• 發現⾴頁⾯面加上以上屬性,在iOS Safari滑動會變很順

• 發現Bug:iOS Safari切換⾴頁⾯面載入Data時,沒有偵測到捲軸,
就會鎖死⾴頁⾯面。即使下⾯面還有內容,也無法往下滑。
Preference: How we fixed the -webkit-overflow-scrolling: touch; bug on iOS
.outer {
overflow: scroll;
-webkit-overflow-scrolling: touch;
/* More properties for a fixed height ... */
}
.inner {
height: calc(100% + 1px);
}
眉⾓角:iOS safari scroll
網站路路由:Vue-router
Preference: Github: Vuejs/vue-router
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '../components/Hello'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello
}
]
})
Vue-cli預設router樣式
…
export default new Router({
mode: 'history',
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
},
…
})
Vue-router:Settings
router/index.js
…
export default new Router({
…
routes: [
{
path: '/',
name: 'index',
component: (resolve) => {
require(['../pages/index.vue'], resolve)
}
},
…
]
})
Vue-router:Lazy Loading Routes
router/index.js
<router-link :to="{ name: 'index' }">連結⽂文字</router-link>
…
export default new Router({
…
routes: [
…
{
path: '/page/:pageId',
name: 'page',
component: (resolve) => {
require(['../pages/page.vue'], resolve)
}
},
…
]
})
Vue-router:Params
router/index.js
<router-link :to="{ name: 'page', params: { pageId: id } }">連結⽂文
字</router-link>
routes: [
…
{
path: '/404',
name: 'unfound',
component: (resolve) => {
require(['../pages/unfound.vue'], resolve)
}
},
{
path: '*',
redirect: to => {
return { name: 'unfound' }
}
}
]
…
Vue-router:Unfound Page
router/index.js
狀狀態管理理:Vuex
$ yarn add vuex
…
import store from './store'
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
main.js─ src/
 ├ …
 ├ store/
  ├ modules/
   ├ app.js
   └ …
  └ index.js
 ├ … 
Preference: Github: Vuejs/vuex
Vuex Modules
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
…
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
app,
…
}
})
store/index.js
Vuex Modules
const state = {
isLoading: false
…
}
const getters = {
isLoading: state => state.isLoading
…
}
const mutations = {
[types.setLoading] (state, boolean) {
state.isLoading = boolean
},
…
}
store/modules/app.js
Vuex Modules
const types = {
setLoading: 'app/setLoading',
…
}
const actions = {
setLodingAction ({commit}, boolean) {
commit(types.setLoading, boolean)
},
…
}
export default {
state,
getters,
mutations,
actions
}
store/modules/app.js
Vuex mapGetters, mapActions
import { mapGetters, mapActions } from 'vuex'
export default {
computed: mapGetters([
'isLoading',
…
]),
methods: {
...mapActions([
'setLodingAction',
…
]),
…
}
}
*.vue
computed: {
isLoading: this.$store.state.app.isLoading
}
methods: {
setLoadingMethod (value) {
this.$store.commit('setLoading', value)
// Commit mutations
}
}
Vuex Devtools
中場休息
import Vue from 'vue'
import VueResource from 'vue-resource'
Vue.use(VueResource)
export const pageResource =
Vue.resource(api_link + '{/params}')
…
api/resource.js─ src/
 ├ …
 ├ api/
  ├ resource.js
  └ index.js
 ├ … 
溝通Server:Vue-resource
$ yarn add vue-resource
Preference: Github: vue-resource
import * as resource from './resources'
export const getPage = (id) => resource.
pageResource.get({ params: id })
export const postPage = (object) => resource.
pageResource.save(object)
export const putPage = (id, object) => resource.
pageResource.update({ params: id }, object)
export const deletePage = (id) => resource.
pageResource.delete({ params: id })
…
api/index.js
溝通Server:Vue-resource
import { getPage } from '../api'
…
export default {
…
methods: {
fetchData () {
let id = this.$route.params.pageId // vue-router params
getPage(id).then(response => {
response.json().then(json => {
this.data = json
})
}, (response) => {
response.json().then(json => {
this.errorMsg(json)
})
})
}
}
}
pages/*.vue
Vue-resource:取得資料
Vue-router:Data Fetching
取得資料顯⽰示在⾴頁⾯面上有兩兩種⽅方式:

• 導入⾴頁⾯面之後獲取

• 導入⾴頁⾯面之前獲取
๏ 需要載入(或進度條)畫⾯面,讓使⽤用者知道正在載入

๏ 第⼆二種⽅方式在beforeRouteEnter取得資料,缺點是這個時候⾴頁
⾯面未載入,不能使⽤用this取得Vue其他資料

๏ 上⼀一⾴頁/下⼀一⾴頁時,這些⾴頁⾯面在v-for載入資料之前就先把卷軸定
位,所以router內建的scrollBehavior沒有⽤用。必須另外處理理。

๏ 以下說明第⼀一種⽅方式
import { mapGetters } from 'vuex'
export default {
computed: mapGetters([
'initial',
…
]),
created () {
this.fetchData()
},
watch: {
'$route' () {
this.fetchData()
}
},
…
}
mixins/routeFetching.vue
Component載入後取得數據
router有其他變動,取得數據
例例如params改變
Vue-router:Data Fetching
Vue-router:Data Fetching
pages/*.vue
import routeLoading from '../mixins/routeFetching'
export default {
…
mixins: [
routeLoading
]
…
}
…
眉⾓角:Data Fetching順序
App.vue router-view
新⾴頁⾯面/重新整理理

取得預先資料
created () 同時執⾏行行
initial = false
不執⾏行行fetchData
vuex
initial = false
已取得資料 initial = true watch initial變動
initial = true
fetchData
切換不同route initial = true created () 執⾏行行

initial = true
fetchData
切換同⼀一route

params變動
initial = true watch $route變動

initial = true
fetchData
initial 是 App.vue最外⾯面那層獲取
資料之後(例例如辨別會員是否登入)

,由false → true
import { mapGetters } from 'vuex'
export default {
computed: mapGetters([
'initial',
…
]),
created () {
this.startFetch(this.initial)
},
watch: {
'$route' () {
this.startFetch(this.initial)
},
'initial' (value) {
this.startFetch(value)
}
},
mixins/routeFetching.vue
methods: {
startFetch (initial) {
if (initial) {
this.fetchData()
}
}
}
}
眉⾓角:Data Fetching順序
眉⾓角:⾴頁⾯面Loading狀狀態
export default {
…
data () {
return {
loading: false
}
}
…
}
mixins/routeFetching.vue
<template>
<div>
<loading v-if="loading"></loading>
<div v-else>
// 顯⽰示的資料
</div>
</div>
</template>
pages/*.vue
眉⾓角:⾴頁⾯面Loading狀狀態
export default {
…
methods: {
fetchData () {
let id = this.$route.params.pageId // vue-router params
this.loading = true // loading true
getPage(id).then(response => {
response.json().then(json => {
this.data = json
this.loading = false // loading false
})
}, (response) => {
response.json().then(json => {
this.errorMsg(json) // in errorMsg redirect error.
})
})
},
…
}
}
pages/*.vue
眉⾓角:⾴頁⾯面Loading狀狀態
眉⾓角:ScrollPosition
• 離開⾴頁⾯面之前,儲存scrollPosition

• 偵測上⼀一⾴頁、下⼀一⾴頁啟動時機,popstate = true

• loading = true、讀取本⾴頁資料

• popstate = false、loading = false

• v-for讀取值產⽣生⾴頁⾯面

• 跳⾄至此⾴頁⾯面儲存的最後⼀一個scrollPosition

• 如果沒有偵測到上⼀一⾴頁下⼀一⾴頁,loading = false
import { mapActions } from 'vuex'
export default {
…
beforeRouteLeave (to, from, next) {
this.savePostionAction({
path: from.path,
pos: window.scrollY
})
next()
},
methods: {
...mapActions([
'savePostionAction'
]),
…
}
}
mixins/routeFetching.vue
眉⾓角:ScrollPosition
export default {
…
beforeMount () {
…
let self = this
window.onpopstate = function (event) {
self.setPopstateAction(true)
}
},
…
methods: {
...mapActions([
'setPopstateAction',
…
]),
…
}
}
App.vue
眉⾓角:ScrollPosition
pages/*.vue
export default {
…
methods: {
fetchData () {
let id = this.$route.params.pageId // vue-router params
this.loading = true // loading true
getPage(id).then(response => {
response.json().then(json => {
this.data = json
this.getPagePosition() // detect page position
})
}, (response) => {
response.json().then(json => {
this.errorMsg(json) // in errorMsg set loading false
})
})
},
…
}
}
眉⾓角:ScrollPosition
mixins/routeFetching.vue
…
export default {
…
computed: mapGetters([
'popstate'
…
]),
methods: {
getPagePosition () {
let path = this.$route.path
let pagePositions = this.savedPostions.filter(object => {
if (object.name === name) {
return object
}
})
// 接下⾴頁
眉⾓角:ScrollPosition
mixins/routeFetching.vue
let length = pagePositions.length
if (length > 0 && this.popstate) {
let lastPosition = pagePositions[length - 1]
this.setPopstateAction(false)
setTimeout(function () {
window.scrollTo(0, lastPosition.pos)
}, 10)
}
this.loading = false
}
…
}
}
眉⾓角:ScrollPosition
缺點:

• 連續按上⼀一⾴頁時,如遇到相同路路徑的⾴頁⾯面,取得的
ScrollPosition可能不會是正確的。

• 重新整理理之後,Vuex儲存的ScrollPosition會清空。這時上⼀一⾴頁
會因為沒有紀錄所以置頂。
眉⾓角:ScrollPosition
Ending & QA
Thanks for listening.

More Related Content

PDF
Vue js 大型專案架構
PDF
第一次用 Vue.js 就愛上 [改]
PPTX
Angular 2.0 Pipes
PDF
Routing in NEXTJS.pdf
PPTX
머신러닝 + 주식 삽질기
PDF
Slide 1 - Thiết kế Web cơ bản
PDF
Support developpement applications mobiles avec ionic v3 et v4
PDF
Bài 5 PHẦN MỀM HỆ THỐNG
Vue js 大型專案架構
第一次用 Vue.js 就愛上 [改]
Angular 2.0 Pipes
Routing in NEXTJS.pdf
머신러닝 + 주식 삽질기
Slide 1 - Thiết kế Web cơ bản
Support developpement applications mobiles avec ionic v3 et v4
Bài 5 PHẦN MỀM HỆ THỐNG

What's hot (17)

PDF
[오픈소스컨설팅]J boss6 7_교육자료
PDF
Devwork.vn Tài liệu lập trình PHP Laravel
PPTX
Express JS
PDF
Comment pirater le site de mon concurrent.. et securiser le mien
PPTX
SDR Benchmarks
PDF
Java Server Faces 2
PDF
SEO Manager Resume
KEY
SWTBot Tutorial
PDF
Jsf 110530152515-phpapp01
PDF
Cours design pattern m youssfi partie 5 adapter
PDF
Examen principal - PHP
DOC
BTL Lập trình C#
PDF
SULTHAN's PHP, MySQL & wordpress
PDF
NodeJS for Beginner
PPTX
Module 2 introduction à asp.net web forms
PPTX
Google adsense
PDF
Sample SEO Audit Report
[오픈소스컨설팅]J boss6 7_교육자료
Devwork.vn Tài liệu lập trình PHP Laravel
Express JS
Comment pirater le site de mon concurrent.. et securiser le mien
SDR Benchmarks
Java Server Faces 2
SEO Manager Resume
SWTBot Tutorial
Jsf 110530152515-phpapp01
Cours design pattern m youssfi partie 5 adapter
Examen principal - PHP
BTL Lập trình C#
SULTHAN's PHP, MySQL & wordpress
NodeJS for Beginner
Module 2 introduction à asp.net web forms
Google adsense
Sample SEO Audit Report
Ad

Similar to 以Vue開發電子商務網站
架構與眉角 (20)

PDF
Death of a Themer
PDF
Webpack Encore - Asset Management for the rest of us
PPTX
Nodejs.meetup
KEY
[Coscup 2012] JavascriptMVC
KEY
PPTX
Vue js and Dyploma
PPTX
Vue business first
PPT
nodejs tutorial foor free download from academia
PDF
125 고성능 web view-deview 2013 발표 자료_공유용
PPTX
An introduction to Vue.js
PPTX
How to Build SPA with Vue Router 2.0
PPTX
Django + Vue, JavaScript de 3ª generación para modernizar Django
KEY
Express Presentation
PDF
Fisl 11 - Dicas de Desenvolvimento Web com Ruby
PDF
Django at the Disco
PDF
The Future of CSS with Web components
PDF
The Future of CSS with Web Components
PDF
Vue 淺談前端建置工具
PDF
20130528 solution linux_frousseau_nopain_webdev
PDF
jQuery Makes Writing JavaScript Fun Again (for HTML5 User Group)
Death of a Themer
Webpack Encore - Asset Management for the rest of us
Nodejs.meetup
[Coscup 2012] JavascriptMVC
Vue js and Dyploma
Vue business first
nodejs tutorial foor free download from academia
125 고성능 web view-deview 2013 발표 자료_공유용
An introduction to Vue.js
How to Build SPA with Vue Router 2.0
Django + Vue, JavaScript de 3ª generación para modernizar Django
Express Presentation
Fisl 11 - Dicas de Desenvolvimento Web com Ruby
Django at the Disco
The Future of CSS with Web components
The Future of CSS with Web Components
Vue 淺談前端建置工具
20130528 solution linux_frousseau_nopain_webdev
jQuery Makes Writing JavaScript Fun Again (for HTML5 User Group)
Ad

Recently uploaded (20)

PPTX
Transform Your Business with a Software ERP System
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
Understanding Forklifts - TECH EHS Solution
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PPTX
history of c programming in notes for students .pptx
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PPTX
ISO 45001 Occupational Health and Safety Management System
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PPTX
L1 - Introduction to python Backend.pptx
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
Transform Your Business with a Software ERP System
How to Migrate SBCGlobal Email to Yahoo Easily
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PTS Company Brochure 2025 (1).pdf.......
Understanding Forklifts - TECH EHS Solution
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
history of c programming in notes for students .pptx
Navsoft: AI-Powered Business Solutions & Custom Software Development
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
ISO 45001 Occupational Health and Safety Management System
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
L1 - Introduction to python Backend.pptx
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...

以Vue開發電子商務網站
架構與眉角

  • 2. • Mayu Chen • Frontend Developer of gelovery.com / patio.tw • windfish27@gmail.com ๏ 以下範例例為經驗分享 ๏ 感謝後端⼤大⼤大的⽀支援...m(_ _)m About me
  • 3. • 專案環境簡介 • 初始專案建置 • Css Style • 網站路路由 • 狀狀態管理理 • 溝通Server • 取得⾴頁⾯面資料 Agenda
  • 4. 專案環境簡介 • Docker • Rails • Vue (SPA) • Node • Yarn • Atom • Package: languague-vue • Chrome extension • Vue.js devtools
  • 5. 專案技術列列表 • Html / Css • Scss (Sass) • Javascript (ES6)、JSON • Vue • Vue-router / Vuex / Vue-resource • Webpack
  • 6. $ npm install -g vue-cli $ vue init webpack vue-new ⾃自訂專案名稱 Template variables:
 https://github.com/vuejs/vue-cli 初始專案建置:Vue-cli Preference: Github: Vuejs/vue-cli
  • 7. This will install Vue 2.x version of the template. For Vue 1.x use: vue init webpack#1.0 vue-new ? Project name vue-new ? Project description A Vue.js project ? Author Meiyu <windfish27+GH@gmail.com> ? Vue build standalone ? Install vue-router? Yes ? Use ESLint to lint your code? Yes ? Pick an ESLint preset Standard ? Setup unit tests with Karma + Mocha? No ? Setup e2e tests with Nightwatch? No vue-cli Generated "vue-new". To get started: cd vue-new npm install npm run dev Documentation can be found at https://vuejs-templates.github.io/ webpack
  • 8. $ npm run build ⟶ Production ready build. $ npm run unit ⟶ Unit tests run. $ npm run e2e ⟶ End-to-end tests. $ cd vue-new $ npm install / yarn $ npm run dev 初始專案建置:Vue-cli
  • 9. 初始專案建置:Vue-cli專案結構 ─ build/ ─ config/ ─ node_modules/ ─ src/  ├ assets/  ├ components/   └ Hello.vue  ├ router/   └ index.js  ├ App.vue  └ main.js ─ static/ ─ index.html ─ package.json ─ yarn.lock
  • 10. 初始專案建置:vue-template <template lang="html"> html or other html loader </template> <script> export default { vue methods } </script> <style lang="css"> css or other css loader </style> *.vue
  • 11. import Vue from 'vue' import App from './App' import router from './ router' new Vue({ el: '#app', router, template: '<App/>', components: { App } }) main.js 初始專案建置:Vue-cli初始檔案 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Project Name</ title> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> </body> </html> index.html
  • 12. 初始專案建置:Vue-cli初始檔案 <template> <div id="#app"> <img src="./assets/logo.png"> <router-view></router-view> <hello></hello> </div> </template> <script> import Hello from './components/Hello' export default { name: 'app', components: { Hello } } </script> App.vue <style lang="css"> … </style>
  • 13. 初始專案建置:⾃自訂專案結構 ─ src/  ├ api/  ├ assets/  ├ components/  ├ mixins/  ├ router/  ├ scss/  ├ store/  ├ pages/  ├ vue-plugin/  ├ App.vue  └ main.js
  • 14. $ npm install node-sass sass-loader $ yarn add node-sass sass-loader <style lang="scss"> .style{ … } </style> <style lang="sass"> .style … </style> Css Style:Scss
  • 15. <style lang="scss"> @import '../scss/function/ function'; .component-style{ … } </style> Css Style:Scss $variables: $color; $width: 640px; … scss/function/_variables.scss @mixin function-name($value) { css-proprety: $value; } … scss/function/_mixins.scss @import 'mixins'; @import 'variables'; scss/function/function.scss *.vue
  • 16. Css Style:Scss <style lang="scss"> @import 'scss/style'; </style> App.vue @import 'base/base'; @import 'function/function'; @import 'plugins/plugins'; @import 'partials/partials'; @import 'components/components'; @import 'views/views'; scss/style.scss
  • 17. 眉⾓角:iOS safari scroll -webkit-overflow-scrolling: touch; // iOS safari scroll smooth • Fixed footer範例例 • 發現⾴頁⾯面加上以上屬性,在iOS Safari滑動會變很順 • 發現Bug:iOS Safari切換⾴頁⾯面載入Data時,沒有偵測到捲軸, 就會鎖死⾴頁⾯面。即使下⾯面還有內容,也無法往下滑。
  • 18. Preference: How we fixed the -webkit-overflow-scrolling: touch; bug on iOS .outer { overflow: scroll; -webkit-overflow-scrolling: touch; /* More properties for a fixed height ... */ } .inner { height: calc(100% + 1px); } 眉⾓角:iOS safari scroll
  • 19. 網站路路由:Vue-router Preference: Github: Vuejs/vue-router router/index.js import Vue from 'vue' import Router from 'vue-router' import Hello from '../components/Hello' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Hello', component: Hello } ] }) Vue-cli預設router樣式
  • 20. … export default new Router({ mode: 'history', scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }, … }) Vue-router:Settings router/index.js
  • 21. … export default new Router({ … routes: [ { path: '/', name: 'index', component: (resolve) => { require(['../pages/index.vue'], resolve) } }, … ] }) Vue-router:Lazy Loading Routes router/index.js <router-link :to="{ name: 'index' }">連結⽂文字</router-link>
  • 22. … export default new Router({ … routes: [ … { path: '/page/:pageId', name: 'page', component: (resolve) => { require(['../pages/page.vue'], resolve) } }, … ] }) Vue-router:Params router/index.js <router-link :to="{ name: 'page', params: { pageId: id } }">連結⽂文 字</router-link>
  • 23. routes: [ … { path: '/404', name: 'unfound', component: (resolve) => { require(['../pages/unfound.vue'], resolve) } }, { path: '*', redirect: to => { return { name: 'unfound' } } } ] … Vue-router:Unfound Page router/index.js
  • 24. 狀狀態管理理:Vuex $ yarn add vuex … import store from './store' new Vue({ el: '#app', router, store, template: '<App/>', components: { App } }) main.js─ src/  ├ …  ├ store/   ├ modules/    ├ app.js    └ …   └ index.js  ├ …  Preference: Github: Vuejs/vuex
  • 25. Vuex Modules import Vue from 'vue' import Vuex from 'vuex' import app from './modules/app' … Vue.use(Vuex) export default new Vuex.Store({ modules: { app, … } }) store/index.js
  • 26. Vuex Modules const state = { isLoading: false … } const getters = { isLoading: state => state.isLoading … } const mutations = { [types.setLoading] (state, boolean) { state.isLoading = boolean }, … } store/modules/app.js
  • 27. Vuex Modules const types = { setLoading: 'app/setLoading', … } const actions = { setLodingAction ({commit}, boolean) { commit(types.setLoading, boolean) }, … } export default { state, getters, mutations, actions } store/modules/app.js
  • 28. Vuex mapGetters, mapActions import { mapGetters, mapActions } from 'vuex' export default { computed: mapGetters([ 'isLoading', … ]), methods: { ...mapActions([ 'setLodingAction', … ]), … } } *.vue computed: { isLoading: this.$store.state.app.isLoading } methods: { setLoadingMethod (value) { this.$store.commit('setLoading', value) // Commit mutations } }
  • 31. import Vue from 'vue' import VueResource from 'vue-resource' Vue.use(VueResource) export const pageResource = Vue.resource(api_link + '{/params}') … api/resource.js─ src/  ├ …  ├ api/   ├ resource.js   └ index.js  ├ …  溝通Server:Vue-resource $ yarn add vue-resource Preference: Github: vue-resource
  • 32. import * as resource from './resources' export const getPage = (id) => resource. pageResource.get({ params: id }) export const postPage = (object) => resource. pageResource.save(object) export const putPage = (id, object) => resource. pageResource.update({ params: id }, object) export const deletePage = (id) => resource. pageResource.delete({ params: id }) … api/index.js 溝通Server:Vue-resource
  • 33. import { getPage } from '../api' … export default { … methods: { fetchData () { let id = this.$route.params.pageId // vue-router params getPage(id).then(response => { response.json().then(json => { this.data = json }) }, (response) => { response.json().then(json => { this.errorMsg(json) }) }) } } } pages/*.vue Vue-resource:取得資料
  • 34. Vue-router:Data Fetching 取得資料顯⽰示在⾴頁⾯面上有兩兩種⽅方式: • 導入⾴頁⾯面之後獲取 • 導入⾴頁⾯面之前獲取 ๏ 需要載入(或進度條)畫⾯面,讓使⽤用者知道正在載入 ๏ 第⼆二種⽅方式在beforeRouteEnter取得資料,缺點是這個時候⾴頁 ⾯面未載入,不能使⽤用this取得Vue其他資料 ๏ 上⼀一⾴頁/下⼀一⾴頁時,這些⾴頁⾯面在v-for載入資料之前就先把卷軸定 位,所以router內建的scrollBehavior沒有⽤用。必須另外處理理。 ๏ 以下說明第⼀一種⽅方式
  • 35. import { mapGetters } from 'vuex' export default { computed: mapGetters([ 'initial', … ]), created () { this.fetchData() }, watch: { '$route' () { this.fetchData() } }, … } mixins/routeFetching.vue Component載入後取得數據 router有其他變動,取得數據 例例如params改變 Vue-router:Data Fetching
  • 36. Vue-router:Data Fetching pages/*.vue import routeLoading from '../mixins/routeFetching' export default { … mixins: [ routeLoading ] … } …
  • 37. 眉⾓角:Data Fetching順序 App.vue router-view 新⾴頁⾯面/重新整理理
 取得預先資料 created () 同時執⾏行行 initial = false 不執⾏行行fetchData vuex initial = false 已取得資料 initial = true watch initial變動 initial = true fetchData 切換不同route initial = true created () 執⾏行行
 initial = true fetchData 切換同⼀一route
 params變動 initial = true watch $route變動
 initial = true fetchData
  • 38. initial 是 App.vue最外⾯面那層獲取 資料之後(例例如辨別會員是否登入)
 ,由false → true import { mapGetters } from 'vuex' export default { computed: mapGetters([ 'initial', … ]), created () { this.startFetch(this.initial) }, watch: { '$route' () { this.startFetch(this.initial) }, 'initial' (value) { this.startFetch(value) } }, mixins/routeFetching.vue methods: { startFetch (initial) { if (initial) { this.fetchData() } } } } 眉⾓角:Data Fetching順序
  • 39. 眉⾓角:⾴頁⾯面Loading狀狀態 export default { … data () { return { loading: false } } … } mixins/routeFetching.vue
  • 40. <template> <div> <loading v-if="loading"></loading> <div v-else> // 顯⽰示的資料 </div> </div> </template> pages/*.vue 眉⾓角:⾴頁⾯面Loading狀狀態
  • 41. export default { … methods: { fetchData () { let id = this.$route.params.pageId // vue-router params this.loading = true // loading true getPage(id).then(response => { response.json().then(json => { this.data = json this.loading = false // loading false }) }, (response) => { response.json().then(json => { this.errorMsg(json) // in errorMsg redirect error. }) }) }, … } } pages/*.vue 眉⾓角:⾴頁⾯面Loading狀狀態
  • 42. 眉⾓角:ScrollPosition • 離開⾴頁⾯面之前,儲存scrollPosition • 偵測上⼀一⾴頁、下⼀一⾴頁啟動時機,popstate = true • loading = true、讀取本⾴頁資料 • popstate = false、loading = false • v-for讀取值產⽣生⾴頁⾯面 • 跳⾄至此⾴頁⾯面儲存的最後⼀一個scrollPosition • 如果沒有偵測到上⼀一⾴頁下⼀一⾴頁,loading = false
  • 43. import { mapActions } from 'vuex' export default { … beforeRouteLeave (to, from, next) { this.savePostionAction({ path: from.path, pos: window.scrollY }) next() }, methods: { ...mapActions([ 'savePostionAction' ]), … } } mixins/routeFetching.vue 眉⾓角:ScrollPosition
  • 44. export default { … beforeMount () { … let self = this window.onpopstate = function (event) { self.setPopstateAction(true) } }, … methods: { ...mapActions([ 'setPopstateAction', … ]), … } } App.vue 眉⾓角:ScrollPosition
  • 45. pages/*.vue export default { … methods: { fetchData () { let id = this.$route.params.pageId // vue-router params this.loading = true // loading true getPage(id).then(response => { response.json().then(json => { this.data = json this.getPagePosition() // detect page position }) }, (response) => { response.json().then(json => { this.errorMsg(json) // in errorMsg set loading false }) }) }, … } } 眉⾓角:ScrollPosition
  • 46. mixins/routeFetching.vue … export default { … computed: mapGetters([ 'popstate' … ]), methods: { getPagePosition () { let path = this.$route.path let pagePositions = this.savedPostions.filter(object => { if (object.name === name) { return object } }) // 接下⾴頁 眉⾓角:ScrollPosition
  • 47. mixins/routeFetching.vue let length = pagePositions.length if (length > 0 && this.popstate) { let lastPosition = pagePositions[length - 1] this.setPopstateAction(false) setTimeout(function () { window.scrollTo(0, lastPosition.pos) }, 10) } this.loading = false } … } } 眉⾓角:ScrollPosition
  • 49. Ending & QA Thanks for listening.