This is a web application imitating the PC side of NetEase Cloud Music. It is built based on Vue + Element UI. The overall page style is relatively simple. The main body of the web page is designed to be similar to opening a window app on the Windows desktop. The main body of the application is a window, which can be accessed through Drag the lower right corner to change the window size. Although it may be a bit strange as a web application to be designed like this, it is not unfeasible. Perhaps a web desktop can be derived in the future, similar to the feeling of a cloud desktop.
It seems to be a pretty good idea. Maybe in the future, you can try to build such a web desktop, provide a basic platform to manage the life cycle of each window, and then develop web applications based on this platform and put your own web applications on it.
The project backend comes from NetEase Cloud Music NodeJS version API and the complete interface document of the project
The interface documentation page of this project is no longer accessible. I generated an offline document, which you can download from here.
There are still some pages of the project that have not been completed, but the main pages have been completed, and the project will be continuously updated and deployed on my NetEase Cloud Music (imitation)
Since the server is a domestic server, and the domain name resolution to the domestic host requires registration, and because I just can't pass the registration because I don't have a residence permit, I can only access it directly using IP.
This section will describe how to make this project work properly
$ git clone https://github.com/Binaryify/NeteaseCloudMusicApi.git
$ npm install
The default port for server startup is 3000. If you do not want to use port 3000, you can use the following command: windows
$ set PORT=4000
Under Mac/Linux
$ PORT=4000
$ cd NeteaseCloudMusicApi
$ node app.js
$ git clone https://github.com/ColorlessWin/cloud_music.git
$ npm install
The default server address of the project is http://localhost
and the port is 3000
If you need to modify it, create a new .env.local
file in the root directory of this project and write down the following key-value pairs.
VUE_APP_HOST=/*这里填你的服务器地址(需要加http或https前缀)*/
VUE_APP_PORT=/*这里填你的服务器端口*/
/**
* 示例
* VUE_APP_HOST=https://webservices.fun
* VUE_APP_PORT=80
*/
$ npm run serve
$ npm run build
This project contains a self-written webpack plug-in. Its function is to automatically upload the built files to the server after the build is completed. However, due to the
.env.local
file configuration, it can only be correct when building on my computer. Find the server and upload the file, so it will report an error when building on your computer, but this will not affect the build of the project
If you are just running it locally, just keep all configurations as default.
This part will introduce you to <Rendering/>
, a core component in the project. This component is used in a large number of pages in the project. Understanding how this component works is an important way to understand most of the source code of this project.
The
<Rendering/>
component is responsible for rendering all data in the project that can be abstracted intoArray<Object>
format. The project has a large amount of such data, such as song lists, singer lists, album lists, comment lists, etc. Data conforming toArray<Object>
format.And the
<Rendering/>
component will also take over the loading of these data, paging processing, etc. What you have to do is very simple. You only need to implement afilling
method and pass it to the<Rendering/>
component through props.
We will introduce this component through a simple page in the project.
This is a MV classification page. By switching different classification tags, the page will show you the corresponding MV list. There is also a simple paging function at the bottom. Let's see how to use <Rendering/>
to conveniently implement these functions
You can try this page first
Bottom pagination
Let’s take a look at the general structure of the source code part of this page.
< template >
< span >地区:</ span >
< simple-radio :options = " areaLabel " v-model = " area " /> < br >
< span >类型:</ span >
< simple-radio :options = " typeLabel " v-model = " type " /> < br >
< span >排序:</ span >
< simple-radio :options = " orderLabel " v-model = " order " /> < br >
< rendering
class = " mvs "
:component = " require('@/components/content/matrices/CommonVideoMatrices').default "
:adapter = " adapter "
:show-creator = " true "
:total = " total "
:filling = " filling "
:unique = " area + type + order "
/>
</ template >
< script >
import ...
export default {
name : " Mv " ,
components : {LArea, Rendering, SimpleRadio},
data () {
return {
total : - 1 ,
area : '全部' ,
type : '全部' ,
order : '上升最快' ,
areaLabel : [ '全部' , '内地' , '港台' , '欧美' , '日本' ],
typeLabel : [ '全部' , '官方版' , '原声' , '现场版' , '网易出品' ],
orderLabel : [ '上升最快' , '最热' , '最新' ],
adapter : { ... }
}
},
methods : {
filling ( offset , limit , first_load ) { ... }
}
}
</ script >
Some content that does not require attention is folded here. For the complete source code, please see here.
You can see that the template part of the page is relatively simple. The first is the three <simple-radio/>
components. Their functions are very simple. The corresponding labels are rendered through the three Label arrays defined in data
, and when the labels are clicked Then update the corresponding bound properties through v-model
, and then a <rendering/>
component with many props bound to it.
<rendering/>
Component details It seems that <rendering/>
has a lot of props, but it is not the case. <rendering/>
only has 2 props, and other props will be passed to its internal <component/>
and <pagination/>
< template >
< div >
< component
:is = " component "
v-bind = " Object.assign(props, $attrs) "
v-on = " $listeners "
/>
< pagination
v-model = " props.datas "
v-on = " $listeners "
v-bind = " $attrs "
:filling = " filling "
/>
</ div >
</ template >
< script >
import Pagination from " @/components/common/Pagination " ;
export default {
name : " Rendering " ,
components : {Pagination},
props : {
component : { type : [ Object , Function ], required : true },
filling : { type : Function , required : true },
},
data () {
return {
props : {
datas : [],
}
}
}
}
</ script >
<Rendering/>
source code snippet, some content that does not need attention has been deleted here. For the complete source code, please see here
<pagination/>
is a paging component. It is responsible for rendering a paging component to provide interaction and is also responsible for managing the loading of data.
<component/>
is responsible for loading the components you pass in through the component
prop. In this MV page, I dynamically pass a CommonVideoMatrices
component to component
through require([path]).default
. component
and you can see that I proxy the events inside CommonVideoMatrices
through v-on="$listeners"
, which means you can directly listen to the $emit
event inside CommonVideoMatrices
on <rendering/>
CommonVideoMatrices
is responsible for rendering an actual MV display list. It is actually responsible for displaying data on this page. It internally accepts a prop ofdatas
(datas
should always be data inArray<Object>
format) and renders it throughdatas
. pageThere are many components in the project that are similar to the
CommonVideoMatrices
design. They all render their own data through adatas
prop. Only one component containingdatas
prop can be passed in<rendering/>
These components are located insrc/cmoponents/content/tracks
and undersrc/component/content/matrices
<Pagination/>
will render a paging component on the page to provide interactionThis paging component will only be rendered when you provide the prop
total
. Otherwise, it will not be rendered, but you can still manage the loading of data. For more details about<Pagination/>
you can view the source code.
The above introduces the internal structure and some details of the <Rendering/>
component. At least we know that through component
prop, we can pass a component containing datas
prop into it. <Rendering/>
will help us render this component, but who will give this component datas
prop passes data, through what method?
This brings up another prop filling
within the <Rendering/>
component.
Unlike other props, filling
you need to pass a function to it. This function will be used to load data. It will be automatically called when needed and is required to return a Promise.
We can see how this function is implemented in the MV page
methods: {
filling ( offset , limit , first_load ) {
return new Promise ( resolve => {
mvs ( this . area , this . type , this . order , offset , limit )
. then ( result => {
if ( first_load ) this . total = result [ 'count' ]
resolve ( result [ 'data' ] )
} )
} )
}
}
This function will be passed as a parameter to
<rendering/>
and its internals will be passed to<pagination/>
and it will decide when to call it.
mvs(area, type, order, offset, limit)
is an interface for back-end mv data. The first three parameters are used to determine what type of mv,offset
andlimit
are used for paging.
When the paging component rendered on the page by <pagination/>
is clicked, the filling method is called internally and some parameters are passed. These parameters are used as paging parameters by the mvs
interface and are passed through resolve when the interface data is returned successfully. To the inside of <pagination/>
, the data will be cached this time, and the data will be passed to CommonVideoMatrices
through <Rendering/>
so that the data can be rendered normally.
Filling will also be called when the page is first loaded.
You can see that our page also needs to reload new data after the user selects other tags or categories. You may think of listening to the click event of <simple-radio/>
and then notifying <pagination/>
call in some way. Filling method updates data?
Need not! ! We have a simpler way to implement this function
< rendering
...
:unique =" area + type + order "
/>
unique
will eventually be passed to<pagination/>
area
type
order
They are all bound to three different<simple-radio/>
throughv-model
I just need to add a unique
prop on the <rendering/>
component and pass it a value that is used to respond to data updates. When the value passed to unique
changes, filling will be called. This will be very useful. We often encounter In this scenario, for example, when the id of the playlist is changed, new playlist data is reloaded. At this time, we only need to pass the id to unique
and implement a filling method. When the id changes, the new song will be automatically loaded. Single data.
You can see that <Rendering/>
is very convenient to use in this page. When writing this page, we can only focus on the content of CommonVideoMatrices
without thinking about the data acquisition method and logic. In fact, the data in this page is loading. A loading... animation effect will be displayed. These are also completed by <Rendering/>
, but this part has been streamlined in the code snippet shown here.
In fact, there is another thing called
adapter
that is used to solve the problem of the backend returning the same type of data in different places but with different data structures, but I will not introduce it here.
This is a project for novices. I hope it can give some inspiration and reference to some students who are new to front-end/Vue and can’t find any project practice. Many places in the project are implemented in this way. I believe that after reading this part, Can have a clearer understanding of part of the source code of this project