项目开发:地理定位博客与在线商店
地理定位博客开发
地图与标记设置
在
BlogMap.vue
组件中,需要映射新的
setBounds
操作,并添加
map
引用和
idle
事件监听器。具体代码如下:
<googlemaps-map
ref="map"
:center="center"
:zoom="zoom"
:options="mapOptions"
@update:center="setCenter"
@update:zoom="setZoom"
@click="onMapClick"
@idle="onIdle"
>
同时,添加对应的
onIdle
方法来调度
setBounds
操作并传递地图边界:
onIdle () {
this.setBounds(this.$refs.map.getBounds())
},
之后,使用
googlemaps-marker
循环遍历帖子并为每个帖子显示一个标记:
<googlemaps-marker
v-for="post of posts"
:key="post._id"
:label="{
color: post === currentPost ? 'white' : 'black',
fontFamily: 'Material Icons',
fontSize: '20px',
text: 'face',
}"
:position="post.position"
:z-index="5"
@click="selectPost(post._id)"
/>
刷新应用程序,当平移或缩放地图时,可在开发工具中查看帖子的变更。
登录与注销功能
注销操作
在
posts
Vuex 模块中添加
logout
操作以清除帖子获取数据:
logout ({ commit }) {
commit('posts', {
posts: [],
mapBounds: null,
})
},
在主存储(
store/index.js
文件)的
logout
操作中调用该操作:
logout ({ commit, dispatch }) {
commit('user', null)
$fetch('logout')
// ...
dispatch('posts/logout')
},
为了优化代码,可以将
posts
命名子模块的
logout
操作定义为根操作:
logout: {
handler ({ commit }) {
commit('posts', {
posts: [],
mapBounds: null,
})
},
root: true,
},
然后可以从主存储的
logout
操作中移除
dispatch('posts/logout')
行。
登录操作
当用户成功登录时,调度非命名空间的
logged-in
操作。在
posts
模块中添加
logged-in
操作:
'logged-in': {
handler ({ dispatch, state }) {
if (state.mapBounds) {
dispatch('fetchPosts', {
mapBounds: state.mapBounds,
force: true,
})
}
if (state.selectedPostId) {
dispatch('selectPost', state.selectedPostId)
}
},
root: true,
},
在主存储的
login
操作中,如果用户成功认证,则调度此新的
logged-in
操作:
if (user) {
// ...
dispatch('logged-in')
}
帖子选择与详情展示
帖子详情准备
在
posts
Vuex 模块中,为帖子详情做准备,添加
selectedPostDetails
数据属性、对应的 getter 和 mutation:
state () {
return {
// ...
// Fetched details for the selected post
selectedPostDetails: null,
}
},
getters: {
// ...
selectedPostDetails: state => state.selectedPostDetails,
},
mutations: {
// ...
selectedPostDetails (state, value) {
state.selectedPostDetails = value
},
},
在
selectPost
操作中,通过向服务器的
/post/<id>
路由发出请求来获取详情:
async selectPost ({ commit }, id) {
commit('selectedPostDetails', null)
commit('selectedPostId', id)
const details = await $fetch(`posts/${id}`)
commit('selectedPostDetails', details)
},
同时添加新的
unselectPost
操作:
unselectPost ({ commit }) {
commit('selectedPostId', null)
},
帖子内容组件
创建
content/PostContent.vue
组件来显示帖子的标题、内容、位置信息和评论列表。初始模板如下:
<template>
<div class="post-content">
<template v-if="details">
<div class="title">
<img :src="details.author.profile.photos[0].value" />
<span>
<span>{{ details.title }}</span>
<span class="info">
<span class="name">
{{ details.author.profile.displayName }}</span>
<span class="date">{{ details.date | date }}</span>
</span>
</span>
</div>
<div class="content">{{ details.content }}</div>
<!-- TODO Comments -->
<div class="actions">
<button
type="button"
class="icon-button secondary"
@click="unselectPost">
<i class="material-icons">close</i>
</button>
<!-- TODO Comment input -->
</div>
</template>
<div class="loading-animation" v-else>
<div></div>
</div>
</div>
</template>
脚本部分如下:
<script>
import { createNamespacedHelpers } from 'vuex'
// posts module
const {
mapGetters: postsGetters,
mapActions: postsActions,
} = createNamespacedHelpers('posts')
export default {
computed: {
...postsGetters({
details: 'selectedPostDetails',
}),
},
methods: {
...postsActions([
'unselectPost',
]),
},
}
</script>
位置信息与作用域插槽
作用域插槽概念
作用域插槽允许组件将数据传递给嵌入在插槽中的视图。例如:
<template>
<div class="search">
<slot :results="results" />
</div>
</template>
<script>
export default {
computed: {
results () {
return /* ... */
},
},
}
</script>
使用时可以通过
slot-scope
属性获取作用域数据:
<Search>
<template slot-scope="props">
<div>{{props.results.length}} results</div>
</template>
</Search>
组件实现
创建
PlaceDetails.vue
组件来显示位置的名称和地址:
<script>
export default {
props: {
name: String,
address: String,
},
render (h) {
return <div class="details">
<div class="name"><i class="material-icons">place</i>
{this.name}</div>
<div class="address"> {this.address}</div>
</div>
},
}
</script>
实现
LocationInfo.vue
组件,根据帖子是否有 Google Maps 的
placeId
来选择使用
googlemaps-place-details
或
googlemaps-geocoder
组件:
<template>
<div class="location-info" v-if="currentPost">
<!-- Place -->
<googlemaps-place-details
v-if="currentPost.placeId"
:request="{
placeId: currentPost.placeId
}">
<PlaceDetails
slot-scope="props"
v-if="props.results"
:name="props.results.name"
:address="props.results.formatted_address" />
</googlemaps-place-details>
<!-- Position only -->
<googlemaps-geocoder
v-else
:request="{
location: currentPost.position,
}">
<PlaceDetails
slot-scope="props"
v-if="props.results"
:name="props.results[1].placeDetails.name"
:address="props.results[0].formatted_address" />
</googlemaps-geocoder>
</div>
<div v-else></div>
</template>
脚本部分如下:
<script>
import PlaceDetails from './PlaceDetails.vue'
import { createNamespacedHelpers } from 'vuex'
// posts module
const {
mapGetters: postsGetters,
} = createNamespacedHelpers('posts')
export default {
components: {
PlaceDetails,
},
computed: postsGetters([
'currentPost',
]),
}
</script>
评论与函数式组件
存储变更
在
posts
Vuex 模块中添加新的
addComment
突变以直接向帖子添加评论:
addComment (state, { post, comment }) {
post.comments.push(comment)
},
添加新的
sendComment
操作,向服务器的
/posts/<id>/comment
路由发送查询并将其添加到所选帖子:
async sendComment({ commit, rootGetters }, { post, comment }) {
const user = rootGetters.user
commit('addComment', {
post,
comment: {
...comment,
date: new Date(),
user_id: user._id,
author: user,
},
})
await $fetch(`posts/${post._id}/comment`, {
method: 'POST',
body: JSON.stringify(comment),
})
},
函数式组件
创建
Comment.vue
函数式组件:
<script>
import { date } from '../../filters'
export default {
functional: true,
render (h, { props }) {
const { comment } = props
return <div class="comment">
<img class="avatar" src=
{comment.author.profile.photos[0].value} />
<div class="message">
<div class="info">
<span class="name">{comment.author.profile.displayName}
</span>
<span class="date">{date(comment.date)}</span>
</div>
<div class="content">{comment.content}</div>
</div>
</div>
},
}
</script>
在
PostContent
组件中添加评论列表和评论表单:
<div class="comments">
<Comment
v-for="(comment, index) of details.comments"
:key="index"
:comment="comment" />
</div>
<div class="actions">
<!-- ... -->
<input
v-model="commentContent"
placeholder="Type a comment"
@keyup.enter="submitComment" />
<button
type="button"
class="icon-button"
@click="submitComment"
:disabled="!commentFormValid">
<i class="material-icons">send</i>
</button>
</div>
脚本部分如下:
import Comment from './Comment.vue'
export default {
components: {
Comment,
},
data () {
return {
commentContent: '',
}
},
computed: {
...postsGetters({
details: 'selectedPostDetails',
}),
commentFormValid () {
return this.commentContent
},
},
methods: {
...postsActions([
'sendComment',
'unselectPost',
]),
async submitComment () {
if (this.commentFormValid) {
this.sendComment({
post: this.details,
comment: {
content: this.commentContent,
},
})
this.commentContent = ''
}
},
},
}
地理定位博客开发流程
graph LR
A[地图与标记设置] --> B[登录与注销功能]
B --> C[帖子选择与详情展示]
C --> D[位置信息与作用域插槽]
D --> E[评论与函数式组件]
在线商店项目搭建
项目设置
-
使用
vue init命令生成新项目:
vue init webpack-simple e-shop
cd e-shop
npm install
npm install -S babel-polyfill
-
安装
stylus:
npm i -D stylus stylus-loader
-
移除
src文件夹的内容,下载源文件(https://github.com/Akryum/packt-vue-project-guide/tree/master/chapter7-download/src)并提取到src文件夹中。 - 安装其他依赖包:
npm i -S axios vue-router vuex vuex-router-sync
开发 API 生成
-
安装
json-server作为开发依赖:
npm i -D json-server
-
下载
db.json文件(https://github.com/Akryum/packt-vue-project-guide/blob/master/chapter7-download/db.json)并将其放在项目根目录中。 -
在
package.json文件中添加新的db脚本:
"db": "json-server --watch db.json"
运行命令
npm run db
启动
json-server
,默认监听端口 3000。
应用启动
打开新终端,使用
npm run dev
启动应用程序。
CSS 自动前缀
使用 PostCSS 库进行 CSS 后处理,它具有模块化架构,通过添加插件来以各种方式处理 CSS,可自动添加供应商前缀以提高 CSS 代码与大多数浏览器的兼容性。
在线商店项目搭建流程
graph LR
A[项目设置] --> B[开发 API 生成]
B --> C[应用启动]
C --> D[CSS 自动前缀]
通过以上步骤,我们完成了地理定位博客和在线商店项目的部分开发,后续还可以进一步完善和优化这些项目。例如,在地理定位博客中可以显示帖子标记上的点赞数、允许编辑或删除评论、添加实时更新;在在线商店项目中可以继续深入学习服务器端渲染、国际化、测试和部署等高级主题。
在线商店的高级开发与优化
代码兼容性与质量提升
PostCSS 与自动前缀
在开发在线商店时,为了确保 CSS 代码能兼容大多数浏览器,我们使用 PostCSS 进行后处理。PostCSS 是一个专门用于 CSS 后处理的库,具有模块化架构,可通过添加插件来处理 CSS。以下是使用 PostCSS 和 autoprefixer 优化 CSS 代码兼容性的步骤:
1.
安装依赖
:在项目中安装 PostCSS 和 autoprefixer。
npm install postcss autoprefixer --save-dev
-
配置 PostCSS
:在项目根目录下创建
postcss.config.js文件,并进行如下配置:
module.exports = {
plugins: {
autoprefixer: {}
}
}
- 使用 PostCSS 处理 CSS :可以在构建工具(如 Webpack)中配置 PostCSS 加载器,确保 CSS 文件在构建过程中经过 PostCSS 处理。以下是一个简单的 Webpack 配置示例:
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
]
}
}
ESLint 代码检查
为了提高代码质量和风格一致性,我们使用 ESLint 对代码进行检查。以下是配置 ESLint 的步骤:
1.
安装 ESLint
:在项目中安装 ESLint。
npm install eslint --save-dev
-
初始化 ESLint 配置
:运行以下命令初始化 ESLint 配置文件
.eslintrc.js。
npx eslint --init
在初始化过程中,根据项目需求选择合适的配置选项,如使用的 JavaScript 风格、是否使用框架等。
3.
配置 ESLint 规则
:在
.eslintrc.js
文件中,可以根据项目需求自定义 ESLint 规则。例如:
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:vue/essential"
],
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "single"],
"semi": ["error", "always"]
}
}
-
运行 ESLint 检查
:在
package.json文件中添加 ESLint 脚本,方便运行检查。
{
"scripts": {
"lint": "eslint src"
}
}
运行
npm run lint
命令即可对
src
目录下的代码进行检查。
单元测试
为了确保 Vue 组件的正确性和稳定性,我们可以使用单元测试框架对组件进行测试。以下是使用 Jest 和 Vue Test Utils 进行单元测试的步骤:
1.
安装测试依赖
:在项目中安装 Jest 和 Vue Test Utils。
npm install jest @vue/test-utils --save-dev
-
配置 Jest
:在项目根目录下创建
jest.config.js文件,并进行如下配置:
module.exports = {
preset: '@vue/cli-plugin-unit-jest',
testMatch: [
'**/__tests__/**/*.js',
'**/?(*.)+(spec|test).js'
]
}
-
编写测试用例
:在
__tests__目录下创建测试文件,例如Button.spec.js,并编写测试用例。
import { mount } from '@vue/test-utils'
import Button from '@/components/Button.vue'
describe('Button.vue', () => {
it('renders button text', () => {
const wrapper = mount(Button, {
propsData: {
text: 'Click me'
}
})
expect(wrapper.text()).toContain('Click me')
})
})
-
运行测试
:在
package.json文件中添加测试脚本。
{
"scripts": {
"test": "jest"
}
}
运行
npm run test
命令即可执行单元测试。
国际化与代码分割
国际化
为了让在线商店支持多语言,我们可以使用 Vue I18n 进行国际化处理。以下是配置 Vue I18n 的步骤:
1.
安装 Vue I18n
:在项目中安装 Vue I18n。
npm install vue-i18n
-
创建语言文件
:在
src/locales目录下创建不同语言的 JSON 文件,例如en.json和zh.json。
// en.json
{
"welcome": "Welcome to our online shop!"
}
// zh.json
{
"welcome": "欢迎来到我们的在线商店!"
}
-
配置 Vue I18n
:在
src/main.js文件中配置 Vue I18n。
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import en from './locales/en.json'
import zh from './locales/zh.json'
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: 'en',
messages: {
en,
zh
}
})
new Vue({
i18n,
render: h => h(App)
}).$mount('#app')
-
在组件中使用国际化文本
:在组件中使用
$t方法获取国际化文本。
<template>
<div>
<p>{{ $t('welcome') }}</p>
</div>
</template>
代码分割
使用 Webpack 的代码分割功能可以提高应用的加载性能。以下是一个简单的代码分割示例:
// 在路由配置中使用动态导入
const Home = () => import(/* webpackChunkName: "home" */ './views/Home.vue')
const About = () => import(/* webpackChunkName: "about" */ './views/About.vue')
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
]
服务器端渲染与生产构建
服务器端渲染
使用 Node.js 实现服务器端渲染可以提高应用的 SEO 性能和首屏加载速度。以下是实现服务器端渲染的基本步骤:
1.
安装依赖
:安装
vue-server-renderer
和
express
。
npm install vue-server-renderer express
-
创建服务器文件
:在项目根目录下创建
server.js文件。
const express = require('express')
const { createRenderer } = require('vue-server-renderer')
const app = express()
const renderer = createRenderer()
app.get('*', async (req, res) => {
try {
const app = require('./src/App.vue')
const html = await renderer.renderToString(app)
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Online Shop</title>
</head>
<body>
${html}
</body>
</html>
`)
} catch (error) {
res.status(500).send('Internal Server Error')
}
})
const port = process.env.PORT || 3000
app.listen(port, () => {
console.log(`Server is running on port ${port}`)
})
-
运行服务器
:运行
node server.js启动服务器。
生产构建
为了将应用部署到生产环境,需要进行生产构建。在
package.json
文件中添加生产构建脚本。
{
"scripts": {
"build": "webpack --config webpack.prod.js"
}
}
运行
npm run build
命令进行生产构建,生成的文件可以部署到服务器上。
在线商店高级开发流程
graph LR
A[代码兼容性与质量提升] --> B[单元测试]
B --> C[国际化与代码分割]
C --> D[服务器端渲染与生产构建]
总结与展望
通过以上步骤,我们完成了在线商店项目的高级开发,包括代码兼容性优化、代码检查、单元测试、国际化、代码分割、服务器端渲染和生产构建等。这些技术的应用可以提高应用的性能、可维护性和用户体验。
如果想要进一步提升应用,可以考虑以下改进方向:
-
性能优化
:使用缓存技术、压缩文件等方式进一步提高应用的加载速度。
-
功能扩展
:添加购物车、支付功能、商品搜索等更多实用功能。
-
安全加固
:加强应用的安全性,防止 SQL 注入、XSS 攻击等安全问题。
在未来的开发中,我们可以不断探索和应用新的技术,为用户提供更好的在线购物体验。
地理定位博客与在线商店开发
超级会员免费看
10

被折叠的 条评论
为什么被折叠?



