JSX+Vue3+Vant 实现 网易云播放器(一)__Vue.js
发布于 3 年前 作者 banyungong 1864 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy, hydrogen, condensed-night-purple, greenwillow, v-green, vue-pro, healer-readable, mk-cute, jzman, geek-black, awesome-green, qklhk-chocolate

贡献主题:https://github.com/xitu/juejin-markdown-themes

theme: juejin highlight:

配套代码地址

随意切换React和Vue, 你说有没有搞头?

第一篇: 只做环境跟首页的处理

先上效果图

一、Vue3的环境搭建

  1. vue create vue3-demo之后

  2. 按需选择

  3. 回车之后选3.x版本的vue

  4. 把babel,postcss,eslint这些配置文件放哪? 选择放在独立文件夹

  5. 完成Vue3的坏境搭建

二、Vant3的环境搭建

  1. npm i vant@next -S

  2. 按需引入 npm i babel-plugin-import -D

  3. 对于使用 babel7 的用户,可以在 babel.config.js 中配置。地址:根文件夹/babel.config.js

module.exports = {
  presets: [
    '[@vue](/user/vue)/cli-plugin-babel/preset'
  ],
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant'],
    ['[@vue](/user/vue)/babel-plugin-jsx'] // jsx的扩展待会会用到
  ]
};
  1. 对main.js的改造。方法之一,有更好的方法。 位置:src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import { Button, Icon, NavBar, Swipe, SwipeItem, List } from 'vant';
const app = createApp(App);

app.use(Swipe);
app.use(SwipeItem);
app.use(NavBar);
app.use(Button);

app.use(store).use(router).mount('#app');

  1. 完成vant的环境搭建

三、JSX的环境搭建

  1. yarn add -D @vue/babel-plugin-jsx 或者 npm install @vue/babel-plugin-jsx -D

  2. 位置: 根目录/babel.config.js配置

// 之前我们上面的代码
module.exports = {
  presets: [
    '[@vue](/user/vue)/cli-plugin-babel/preset'
  ],
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant'],
    ['[@vue](/user/vue)/babel-plugin-jsx'] 
  ]
};
  1. 完成jsx的配置。可以使用jsx了

四、完成首页

第一步: 对App.vue的进行改造

位置: src/App.vue 改成 App.jsx来测试jsx环境是否成功

// App.jsx
const App = ({
  render(){
    return <router-view/>
  }
});
export default App

第二步: 新建view/home/index.jsx

// view/home/index.jsx
import { reactive } from 'vue'
import './index.scss'

// mock数据tabBar
const tabBarMock = [
    {
        id: 0,
        title: '推荐',
        path: '/recommend'
    },
    {
        id: 1,
        title: '歌手',
        path: '/singer'
    },
    {
        id: 2,
        title: '排行',

    },
];
const Home = ({
    name: 'Home',
    setup(props) {
        const state = reactive({
            list: [],
            tabBarIndex: 0
        });
        // 导航栏搜索
        const handleNavSearch = () => {
            console.log('搜索');
        };
        // 导航栏展开
        const handleNavExtend = () => {
            console.log('展开');
        };
        // tabBar切换
        const handleTabClick = (id) => {
            state.tabBarIndex = id;
        };
        const renderNavRight = <van-icon name="search" size="18" color="#fff" onClick={handleNavSearch}/>;
        const renderNavLeft = <van-icon name="wap-nav" size="18" color="#fff" onClick={handleNavExtend}/>;
        const renderTabBar = () => {
            return (
                <div className="tabBar">
                    {
                        tabBarMock.map(item => {
                            return (
                                <div key={item.id} onClick={() => handleTabClick(item.id)}>
                                    <span className={state.tabBarIndex === item.id ? 'tabBarIndex' : ''}>
                                        {item.title}
                                    </span>
                                </div>
                            )
                        })
                    }
                </div>
            )
        };
        // 最终的渲染
       return () => (
            <div className="viewBox">
                <van-nav-bar
                    title="Pro"
                    v-slots={{
                        'right': () => renderNavRight,
                        'left': () => renderNavLeft
                    }}
                />
                {
                    renderTabBar()
                }
                <div className="wrapBox">
                    <router-view/>
                </div>
            </div>
        )
    },
    
    
});

export default Home

第三步: 新建view/home/index.scss

// view/home/index.scss
// 公共变量
$bgMain: #d44439;
$fontColor: #f1f1f1;

.tabBar{
  display: flex;
  justify-content: space-between;
  text-align: center;
  line-height: 40px;
  background: $bgMain;
  color: $fontColor;
  font-size: 14px;
  div{
    align-items: center;
    flex: 1;
  }
  span{
    border-bottom: 2px solid transparent;
    padding: 3px 0;
    font-weight: 700;
  }
  .tabBarIndex{
    border-bottom: 2px solid #ffffff;
    padding: 3px 0;
  }
}
.van-nav-bar{
  background-color: $bgMain;
  div{
    color: $fontColor;
  }
}
.van-nav-bar::after{
  border-bottom: none;
}

到此、我们的Home模块算完成了。接下来完成他的子路由。通过tabBar来切换路径

看效果图

第四步: 改装router、位置:src/router/index.js

// src/router/index.js
import { createRouter, createWebHashHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/home/index'),
    children: [
      {
        path: '/recommend',
        name: 'recommend',
        component: () => import('../views/recommend/index')
      },
    ],
  },
];

const router = createRouter({
  history: createWebHashHistory(),
  routes
});

export default router

在完成推荐页面之前

我们需要在home/index.jsx补上路由视图跟对路由的监听、后续增加tabBar跳转页面

// home/index.jsx
import { onBeforeMount, reactive } from 'vue';
// 增加路由
import router from '../../router/index';
import './index.scss'

// mock数据tabBar
const tabBarMock = [
    {
        id: 0,
        title: '推荐',
        path: '/recommend'
    },
    {
        id: 1,
        title: '歌手',
        path: '/singer'
    },
    {
        id: 2,
        title: '排行',

    },
];
const Home = ({
    name: 'Home',
    setup(props) {
        const state = reactive({
            list: [],
            tabBarIndex: 0
        });
        // tabBar跳页面
        const handleWhichPath = () => {
            switch (state.tabBarIndex) {
                case 0:
                    router.push('/recommend');
                    break;
                default:
                    break;
            }
        };
        // 导航栏搜索
        const handleNavSearch = () => {
            console.log('搜索');
        };
        // 导航栏展开
        const handleNavExtend = () => {
            console.log('展开');
        };
        // tabBar切换
        const handleTabClick = (id) => {
            state.tabBarIndex = id;
        };
        // 增加tabBar判断
        onBeforeMount(() => {
            handleWhichPath();
        });
        // navBar的左插槽
        const renderNavRight = <van-icon name="search" size="18" color="#fff" onClick={handleNavSearch}/>;
        // navBar的右插槽
        const renderNavLeft = <van-icon name="wap-nav" size="18" color="#fff" onClick={handleNavExtend}/>;
        const renderTabBar = () => {
            return (
                <div className="tabBar">
                    {
                        tabBarMock.map(item => {
                            return (
                                <div key={item.id} onClick={() => handleTabClick(item.id)}>
                                    <span className={state.tabBarIndex === item.id ? 'tabBarIndex' : ''}>
                                        {item.title}
                                    </span>
                                </div>
                            )
                        })
                    }
                </div>
            )
        };
        // 渲染
        return () => (
            <div className="viewBox">
                <van-nav-bar
                    title="Pro"
                    v-slots={{
                        'right': () => renderNavRight,
                        'left': () => renderNavLeft
                    }}
                />
                {
                    renderTabBar()
                }
                <div className="wrapBox">
                    <router-view/>
                </div>
            </div>
        )
    },
});

export default Home

第五步: 新建src/view/recommend/index.jsx

import './index.scss';

// 轮播图mock数据
const swipeMock = [
    {
        id: 0,
        picUrl: 'http://p1.music.126.net/L2vC5tVzEGVehAtcjpZ1FQ==/109951165605310291.jpg',
    },
    {
        id: 1,
        picUrl: 'http://p1.music.126.net/8vLSxjkhuwpgDPErxyVt4w==/109951165609474509.jpg',
    }
];
// listMock数据
const listMock = [
    {
        id: 5344775532,
        type: 0,
        name: "复古系|光怪陆离的幻想之旅",
        copywriter: "编辑推荐:簇拥着的斑斓色彩↵些许蒙上了年代感的色泽",
        picUrl: "https://p2.music.126.net/NoURvJXsYoXrl1HqSoNy3Q==/109951165485850861.jpg",
    },
    {
        id: 5445073692,
        type: 0,
        name: "醉听曲中事,何妨忆前尘",
        copywriter: "编辑推荐: 一曲新词酒一杯,去年天气旧亭台",
        picUrl: "https://p2.music.126.net/G-qDlNXkDzutU-aqaKeAfg==/109951165606205375.jpg",
    },
    {
        id: 2857550594,
        type: 0,
        name: "精选|评论10w+单曲循环歌曲",
        copywriter: "热门推荐",
        picUrl: "https://p2.music.126.net/UuKmzOfFymLTGW77VTAFMw==/109951164170448016.jpg",
    },
    {
        id: 5312159651,
        type: 0,
        name: "请 用 动 物 的 本 能 缠 绕 住 我",
        copywriter: "热门推荐",
        picUrl: "https://p2.music.126.net/5udP0vh4pSTVYTDVOWU7HA==/109951165426321817.jpg",
    },
    {
        id: 3124642208,
        type: 0,
        name: "网易云评论最多的粤语歌TOP50",
        copywriter: "热门推荐",
        picUrl: "https://p2.music.126.net/-6osWky_WObfAydYYiTvpA==/3236962232773608.jpg",
    },
    {
        id: 3228441619,
        type: 0,
        name: "『那些心动的弹唱』钢琴and吉他",
        copywriter: "热门推荐",
        picUrl: "https://p2.music.126.net/ShJ7Kgh0lvGAUE83LTKzQg==/109951165121506557.jpg",
    },
    {
        id: 4932781493,
        type: 0,
        name: "“人多 太吵 我突然就不想笑了.”",
        copywriter: "热门推荐",
        picUrl: "https://p2.music.126.net/0MzdxTmiY0tO4SR4dp6WvA==/109951164845157020.jpg",
    },
    {
        id: 2557744972,
        type: 0,
        name: "先上DJ先上DJ【土到极致就是嗨】",
        copywriter: "热门推荐",
        picUrl: "https://p2.music.126.net/AzQ5PYt2VYAIUM-506fHoQ==/109951165502267501.jpg",
    },
    {
        id: 5344775532,
        type: 0,
        name: "复古系|光怪陆离的幻想之旅",
        copywriter: "编辑推荐:簇拥着的斑斓色彩↵些许蒙上了年代感的色泽",
        picUrl: "https://p2.music.126.net/NoURvJXsYoXrl1HqSoNy3Q==/109951165485850861.jpg",
    },
];

const recommend = ({
    name: 'recommend',
    setup(props) {
        const renderSpace = () => {
            return (
                <div class='spaceTitle'>
                    推荐歌单
                </div>
            )
        };
        const renderSwipe = () => {
            return (
                <van-swipe>
                    {
                        swipeMock.map(item => {
                            return (
                                <van-swipe-item key={item.id} class="swipeImg">
                                    <img src={item.picUrl} alt=""/>
                                </van-swipe-item>
                            )
                        })
                    }
                </van-swipe>
            )
        };
        const renderRecommendList = () => {
            return (
                <div class="recommendList">
                    {
                        listMock.map((item) => {
                            return (
                                <div class="recommendItem" key={item.id}>
                                    <img
                                        src={item.picUrl}
                                        alt=""/>
                                    <div class="desc">
                                        {item.name}
                                    </div>
                                </div>
                            )
                        })
                    }
                </div>
            )
        };
        return() => (
            <div className="recommendPage">
                { renderSwipe() }
                { renderSpace() }
                { renderRecommendList() }
            </div>
        )
    },
});

export default recommend

最后一步: 新建src/view/recommend/index.scss

// 公共变量
$bgMain: #d44439;
$fontColor: #f1f1f1;

.recommendPage{
  height: 100%;
  background: #f2f2f2;
  overflow-y: auto;
  .swipeImg{
    height: 160px;
    width: 100%;
    padding: 4px;
    background-color: #fff;
    img{
      width: 100%;
      height: 100%;
      border-radius: 10px;
    }
  }
  .spaceTitle{
    font-weight: 700;
    padding-left: 6px;
    font-size: 14px;
    line-height: 60px;
  }
  .recommendList{
    width: 100%;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-around;
    overflow-y: auto;
    .recommendItem{
      width: 32%;
      img{
        width: 100%;
        height: 120px;
      }
      .desc{
        overflow: hidden;
        margin-top: 2px;
        padding: 0 2px;
        height: 50px;
        text-align: left;
        font-size: 12px;
        line-height: 1.4;
        color: #2E3030;
      }
    }
  }
}

效果图:

这样就完成第一篇了~

JSX跟Vue3和Vant完美兼容的、希望有带来乐趣

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 耶巴蒂中国 原文链接:https://juejin.im/post/6914997086295916551

回到顶部