18、项目开发:地理定位博客与在线商店

地理定位博客与在线商店开发

项目开发:地理定位博客与在线商店

地理定位博客开发

地图与标记设置

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[评论与函数式组件]

在线商店项目搭建

项目设置

  1. 使用 vue init 命令生成新项目:
vue init webpack-simple e-shop
cd e-shop
npm install
npm install -S babel-polyfill
  1. 安装 stylus
npm i -D stylus stylus-loader
  1. 移除 src 文件夹的内容,下载源文件(https://github.com/Akryum/packt-vue-project-guide/tree/master/chapter7-download/src)并提取到 src 文件夹中。
  2. 安装其他依赖包:
npm i -S axios vue-router vuex vuex-router-sync

开发 API 生成

  1. 安装 json-server 作为开发依赖:
npm i -D json-server
  1. 下载 db.json 文件(https://github.com/Akryum/packt-vue-project-guide/blob/master/chapter7-download/db.json)并将其放在项目根目录中。
  2. 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
  1. 配置 PostCSS :在项目根目录下创建 postcss.config.js 文件,并进行如下配置:
module.exports = {
  plugins: {
    autoprefixer: {}
  }
}
  1. 使用 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
  1. 初始化 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"]
  }
}
  1. 运行 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
  1. 配置 Jest :在项目根目录下创建 jest.config.js 文件,并进行如下配置:
module.exports = {
  preset: '@vue/cli-plugin-unit-jest',
  testMatch: [
    '**/__tests__/**/*.js',
    '**/?(*.)+(spec|test).js'
  ]
}
  1. 编写测试用例 :在 __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')
  })
})
  1. 运行测试 :在 package.json 文件中添加测试脚本。
{
  "scripts": {
    "test": "jest"
  }
}

运行 npm run test 命令即可执行单元测试。

国际化与代码分割

国际化

为了让在线商店支持多语言,我们可以使用 Vue I18n 进行国际化处理。以下是配置 Vue I18n 的步骤:
1. 安装 Vue I18n :在项目中安装 Vue I18n。

npm install vue-i18n
  1. 创建语言文件 :在 src/locales 目录下创建不同语言的 JSON 文件,例如 en.json zh.json
// en.json
{
  "welcome": "Welcome to our online shop!"
}
// zh.json
{
  "welcome": "欢迎来到我们的在线商店!"
}
  1. 配置 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')
  1. 在组件中使用国际化文本 :在组件中使用 $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
  1. 创建服务器文件 :在项目根目录下创建 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}`)
})
  1. 运行服务器 :运行 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 攻击等安全问题。

在未来的开发中,我们可以不断探索和应用新的技术,为用户提供更好的在线购物体验。

"Mstar Bin Tool"是一款专门针对Mstar系列芯片开发的固件处理软件,主要用于智能电视及相关电子设备的系统维护深度定制。该工具包特别标注了"LETV USB SCRIPT"模块,表明其对乐视品牌设备具有兼容性,能够通过USB通信协议执行固件读写操作。作为一款专业的固件编辑器,它允许技术人员对Mstar芯片的底层二进制文件进行解析、修改重构,从而实现系统功能的调整、性能优化或故障修复。 工具包中的核心组件包括固件编译环境、设备通信脚本、操作界面及技术文档等。其中"letv_usb_script"是一套针对乐视设备的自动化操作程序,可指导用户完成固件烧录全过程。而"mstar_bin"模块则专门处理芯片的二进制数据文件,支持固件版本的升级、降级或个性化定制。工具采用7-Zip压缩格式封装,用户需先使用解压软件提取文件内容。 操作前需确认目标设备采用Mstar芯片架构并具备完好的USB接口。建议预先备份设备原始固件作为恢复保障。通过编辑器修改固件参数时,可调整系统配置、增删功能模块或修复已知缺陷。执行刷机操作时需严格遵循脚本指示的步骤顺序,保持设备供电稳定,避免中断导致硬件损坏。该工具适用于具备嵌入式系统知识的开发人员或高级用户,在进行设备定制化开发、系统调试或维护修复时使用。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值