个人简历小程序开发

3,336 阅读4分钟

1、背景

这几天在wx随机搜了关键词“前端简历”,发现了很多个人的在线简历小程序,脑子里突然也想搞一个属于自己的简历小程序。也可以当做自己的个人项目,大家也可以试一下。

2、选框架

开发小程序现在有很多方式可以选择,可以给我们开发带来很多便利,同时也让项目具有一定的扩展性。目前市面上流行的小程序开发方式主要有:原生,腾讯wepy,美团mpvue,O2实验室Taro,DCloud公司Uni-app 等5种.

下图是部分框架简单的对比介绍:

image.png

由于目前公司的业务主要使用的技术栈是 React + Ts,因此使用taro开发小程序再好不过了,上手快,社区活跃度也蛮高的,而且这个框架的目标很伟大,就是用 React 的开发方式编写一次代码,生成能运行在微信小程序、H5、React Native 等的应用,其拓展性可谓是相当好的,因此选定 Taro

3、开始搭建

yarn global add @tarojs/cli taro init my-intro-App 按照自己需要安装插件:typescript\sass cd my-intro-app yarn dev:weapp 以下是项目结构:

├── dist                        编译结果目录
|
├── config                      项目编译配置目录
|   ├── index.js                默认配置
|   ├── dev.js                  开发环境配置
|   └── prod.js                 生产环境配置
|
├── src                         源码目录
|   ├── pages                   页面文件目录
|   |   └── index               index 页面目录
|   |       ├── index.js        index 页面逻辑
|   |       ├── index.css       index 页面样式
|   |       └── index.config.js index 页面配置
|   |
|   ├── app.js                  项目入口文件
|   ├── app.css                 项目总通用样式
|   └── app.config.js           项目入口配置
|
├── project.config.json         微信小程序项目配置 project.config.json
├── project.tt.json             字节跳动小程序项目配置 project.config.json
├── project.swan.json           百度小程序项目配置 project.swan.json
├── project.qq.json             QQ 小程序项目配置 project.config.json
|
├── babel.config.js             Babel 配置
├── tsconfig.json               TypeScript 配置
├── .eslintrc                   ESLint 配置
|
└── package.json
src 目录下创建几个文件夹:api、assets、components、store、styles
  • 将index.scss文件重新命名为style.module.scss,将config/index.js文件中cssModules.enable置为true,即可支持模块化引入样式。
  • 将index.tsx内容改写,因为我习惯使用函数式编程,所以将class改写。
import React,{useEffect} from 'react'
import { View, Text } from '@tarojs/components'
import styles from './style.module.scss';

const Index:React.FC = props=> { 

  useEffect(()=>{ 
    
  })
    return (
      <View className={styles.index}>
        <Text>Hello world!</Text>
      </View>
    ) 
}
export default Index
  • 在src目录下新建几个目录:api、assets、components、store
  • 打开config目录下的index.js文件,设置别名路径如下:
const path = require('path') // eslint-disable-line 别名设置

const config = {
  projectName: 'my-intro-app',
  date: '2021-8-26',
  designWidth: 750,
  deviceRatio: {
    640: 2.34 / 2,
    750: 1,
    828: 1.81 / 2
  },
  sourceRoot: 'src',
  outputRoot: 'dist',
  plugins: [],
  defineConstants: {
  },
  copy: {
    patterns: [
    ],
    options: {
    }
  },
  framework: 'react',
  mini: {
    postcss: {
      pxtransform: {
        enable: true,
        config: {

        }
      },
      url: {
        enable: true,
        config: {
          limit: 1024 // 设定转换尺寸上限
        }
      },
      cssModules: {
        enable: true, // 默认为 false,如需使用 css modules 功能,则设为 true
        config: {
          namingPattern: 'module', // 转换模式,取值为 global/module
          generateScopedName: '[name]__[local]___[hash:base64:5]'
        }
      }
    }
  },
  h5: {
    publicPath: '/',
    staticDirectory: 'static',
    postcss: {
      autoprefixer: {
        enable: true,
        config: {
        }
      },
      cssModules: {
        enable: true, // 默认为 false,如需使用 css modules 功能,则设为 true
        config: {
          namingPattern: 'module', // 转换模式,取值为 global/module
          generateScopedName: '[name]__[local]___[hash:base64:5]'
        }
      }
    }
  },
  alias: {
    '@api': path.resolve(__dirname, '..', 'src/api'),
    '@components': path.resolve(__dirname, '..', 'src/components'),
    '@assets': path.resolve(__dirname, '..', 'src/assets'),
    '@store': path.resolve(__dirname, '..', 'src/store')
  }
}

module.exports = function (merge) {
  if (process.env.NODE_ENV === 'development') {
    return merge({}, config, require('./dev'))
  }
  return merge({}, config, require('./prod'))
}

避免别名引入报错,找到tsconfig.json文件加入以下代码:

    "paths": {
            "@src/*": ["src/*"],
            "@config/*": ["src/config/*"],
            "@store/*": ["src/store/*"],
            "@views/*": ["src/views/*"],
            "@components/*": ["src/components/*"],
            "@services/*": ["src/services/*"],
            "@styles/*": ["src/styles/*"]
    },

• 使用 redux 状态管理 src目录下创建store/index.js文件,写入如下内容:

import { createStore, applyMiddleware, compose } from 'redux'
import thunkMiddleware from 'redux-thunk'
import rootReducer from '@redux/reducers'

const composeEnhancers =
  typeof window === 'object' &&
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
    }) : compose

const middlewares = [
  thunkMiddleware
]

/**
 * redux-logger 中间件,用于在发起 Action 时,在控制台打印 Action 及其前后 Store 中的保存的状态信息
 */
if (process.env.NODE_ENV === 'development' && process.env.TARO_ENV !== 'quickapp') {
  
  middlewares.push(require('redux-logger').createLogger())
}

const enhancer = composeEnhancers(
  applyMiddleware(...middlewares), 
)

export default function configStore () {
  const store = createStore(rootReducer, enhancer)
  return store
}

src目录下创建 redux 目录,在其中分别创建:action目录/login.ts、reducers目录/user.ts、constants/index.ts. • reducers/user.ts代码如下:

import { SET_IS_LOGIN,SET_LOGIN_INFO } from '../constants'

// 初始状态
const INITIAL_STATE = {
    avatar: '', // 头像
    nickName: '', // 微信昵称
    isLogin: false, // 初始登录状态
}

export default function user (state = INITIAL_STATE, action) {
  switch (action.type) {
    case SET_IS_LOGIN:
      // 登录状态
      const isLogin = action.payload
      return {
        ...state,
        isLogin
      };
    case SET_LOGIN_INFO: 
      // 头像和昵称
      const { avatar, nickName } = action.payload
      return {
        ...state,
        avatar,
        nickName
      }
    default:
      return state
  }
}

• action/login.ts代码如下:

import { SET_IS_LOGIN,SET_LOGIN_INFO } from '../constants'

// 设置登录状态
export const setLogin = () => {
  return {
    type: SET_IS_LOGIN
  }
} 

// 设置登录信息
export const setUserInfo = ()=>{
  return {
    type: SET_LOGIN_INFO
  }
}

如何使用? 登录弹窗中使用:

/**
 * 全局登录弹窗
 */
import React, { useEffect } from 'react';
import { Button, View } from '@tarojs/components'
import Taro from '@tarojs/taro'
import styles from './style.module.scss';
import { useSelector,useDispatch } from 'react-redux' 
import { SET_IS_LOGIN,SET_LOGIN_INFO } from '@redux/constants'
import {setStorageSync} from '@utils/storage'  

interface Iprops {
    setLoginHide:()=>void  // 关闭弹窗
}

const LoginModal: React.FC<Iprops> = props => {  
    const {setLoginHide} = props
    
    // 使用 useDispatch
    const dispatch = useDispatch()

    // 检查用户微信是否授权登录
    const getUserfile = () => {
        Taro.getUserProfile({
            // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
            desc: '用于个人资料',
            success: (res) => {
                const {avatarUrl,nickName} = res.userInfo
                
                // 将用户信息存储在缓存中
                setStorageSync('userInfo',{ avatar: avatarUrl, nickName:nickName })
                setStorageSync('isLogin',true)

                // 调用action设置登录状态
                dispatch({ type: SET_IS_LOGIN,payload:{isLogin:true} })
                // 设置redux存储头像和昵称
                dispatch({ type: SET_LOGIN_INFO,payload:{
                    avatar: avatarUrl,
                    nickName: nickName
                } }) 

                // 隐藏登录弹窗
                setLoginHide()
                console.log('获取用户信息',res.userInfo);
            },
            fail: (res) => {
                setStorageSync('isLogin',false)
                Taro.showToast({title:'获取失败',icon:'none'})
                console.log(res);
            },
            complete: (res) => {
                console.log(res);
            }
        })
    } 

    return (
        <View className={styles.lgmodalbox}>
            <View className={styles.box}>
                <View className={styles.txt}>请点击登录</View>
                <Button type="primary" onClick={getUserfile} className={styles.btn}>登录</Button>
            </View>
        </View>
    )
}

export default LoginModal

扫一扫

gh_46ce0beff076_430.jpg